diff --git a/src/gajim.py b/src/gajim.py
index b05d34a35..87784d83d 100644
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -67,6 +67,7 @@ import logging
log = logging.getLogger('gajim.gajim')
import getopt
+from common import i18n
def parseOpts():
profile = ''
@@ -153,6 +154,10 @@ 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
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
@@ -213,18 +218,48 @@ if pritext:
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 re
import signal
+import time
+import math
+
import gtkgui_helpers
+import notify
+import message_control
+
+from chat_control import ChatControlBase
+from chat_control import ChatControl
+from groupchat_control import GroupchatControl
+from groupchat_control import PrivateChatControl
+
+from atom_window import AtomWindow
+from session import ChatControlSession
+
+import common.sleepy
+
+from common.xmpp import idlequeue
+from common.zeroconf import connection_zeroconf
+from common import resolver
+from common import proxy65_manager
+from common import socks5
+from common import helpers
+from common import optparser
+from common import dataforms
+from common import passwords
+from common import pep
gajimpaths = common.configpaths.gajimpaths
pid_filename = gajimpaths['PID_FILE']
+config_filename = gajimpaths['CONFIG_FILE']
import traceback
import errno
@@ -359,8 +394,3258 @@ def on_exit():
import atexit
atexit.register(on_exit)
+parser = optparser.OptionsParser(config_filename)
-from interface import Interface
+import roster_window
+import profile_window
+import config
+from threading import Thread
+
+
+class PassphraseRequest:
+ def __init__(self, keyid):
+ self.keyid = keyid
+ self.callbacks = []
+ self.dialog_created = False
+ self.dialog = None
+ self.completed = False
+
+ def interrupt(self):
+ self.dialog.window.destroy()
+ self.callbacks = []
+
+ def run_callback(self, account, callback):
+ gajim.connections[account].gpg_passphrase(self.passphrase)
+ callback()
+
+ def add_callback(self, account, cb):
+ if self.completed:
+ self.run_callback(account, cb)
+ else:
+ self.callbacks.append((account, cb))
+ if not self.dialog_created:
+ self.create_dialog(account)
+
+ def complete(self, passphrase):
+ self.passphrase = passphrase
+ self.completed = True
+ if passphrase is not None:
+ 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
+
+ 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}
+
+ def _cancel():
+ # user cancelled, continue without GPG
+ self.complete(None)
+
+ def _ok(passphrase, checked, count):
+ result = gajim.connections[account].test_gpg_passphrase(passphrase)
+ if result == 'ok':
+ # passphrase is good
+ self.complete(passphrase)
+ return
+ elif result == 'expired':
+ dialogs.ErrorDialog(_('GPG key expired'),
+ _('Your GPG key has expied, 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)
+ return
+
+ 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)
+ 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_created = True
+
+
+class ThreadInterface:
+ def __init__(self, func, func_args, callback, callback_args):
+ '''Call a function in a thread
+
+ :param func: the function to call in the thread
+ :param func_args: list or arguments for this function
+ :param callback: callback to call once function is finished
+ :param callback_args: list of arguments for this callback
+ '''
+ def thread_function(func, func_args, callback, callback_args):
+ output = func(*func_args)
+ gobject.idle_add(callback, output, *callback_args)
+ Thread(target=thread_function, args=(func, func_args, callback,
+ callback_args)).start()
+
+class Interface:
+
+################################################################################
+### Methods handling events from connection
+################################################################################
+
+ def handle_event_roster(self, account, data):
+ #('ROSTER', account, array)
+ # FIXME: Those methods depend to highly on each other
+ # and the order in which they are called
+ self.roster.fill_contacts_and_groups_dicts(data, account)
+ self.roster.add_account_contacts(account)
+ self.roster.fire_up_unread_messages_events(account)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('Roster', (account, data))
+
+ def handle_event_warning(self, unused, data):
+ #('WARNING', account, (title_text, section_text))
+ dialogs.WarningDialog(data[0], data[1])
+
+ def handle_event_error(self, unused, data):
+ #('ERROR', account, (title_text, section_text))
+ dialogs.ErrorDialog(data[0], data[1])
+
+ def handle_event_information(self, unused, data):
+ #('INFORMATION', account, (title_text, section_text))
+ dialogs.InformationDialog(data[0], data[1])
+
+ def handle_event_ask_new_nick(self, account, data):
+ #('ASK_NEW_NICK', account, (room_jid,))
+ 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
+ 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)
+ else:
+ self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
+ 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):
+ response(account, iq_obj, 'yes')
+
+ sec_msg = _('Do you accept this request?')
+ if gajim.get_number_of_connected_accounts() > 1:
+ 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'))
+
+ def handle_event_error_answer(self, account, array):
+ #('ERROR_ANSWER', account, (id, jid_from, errmsg, errcode))
+ id_, jid_from, errmsg, errcode = array
+ if unicode(errcode) in ('400', '403', '406') and id_:
+ # show the error dialog
+ ft = self.instances['file_transfers']
+ sid = id_
+ if len(id_) > 3 and id_[2] == '_':
+ sid = id_[3:]
+ if sid in ft.files_props['s']:
+ file_props = ft.files_props['s'][sid]
+ if unicode(errcode) == '400':
+ file_props['error'] = -3
+ else:
+ file_props['error'] = -4
+ self.handle_event_file_request_error(account,
+ (jid_from, file_props, errmsg))
+ conn = gajim.connections[account]
+ conn.disconnect_transfer(file_props)
+ return
+ elif unicode(errcode) == '404':
+ conn = gajim.connections[account]
+ sid = id_
+ if len(id_) > 3 and id_[2] == '_':
+ sid = id_[3:]
+ if sid in conn.files_props:
+ file_props = conn.files_props[sid]
+ self.handle_event_file_send_error(account,
+ (jid_from, file_props))
+ conn.disconnect_transfer(file_props)
+ return
+
+ ctrl = self.msg_win_mgr.get_control(jid_from, account)
+ if ctrl and ctrl.type_id == message_control.TYPE_GC:
+ ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
+
+ def handle_event_con_type(self, account, con_type):
+ # ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'plain'
+ gajim.con_types[account] = con_type
+ self.roster.draw_account(account)
+
+ def handle_event_connection_lost(self, account, array):
+ # ('CONNECTION_LOST', account, [title, text])
+ path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'connection_lost.png')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
+ notify.popup(_('Connection Failed'), account, account,
+ 'connection_failed', path, array[0], array[1])
+
+ def unblock_signed_in_notifications(self, account):
+ gajim.block_signed_in_notifications[account] = False
+
+ def handle_event_status(self, account, show): # OUR status
+ #('STATUS', account, show)
+ 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
+ self.instances[account]['online_dialog'][name].destroy()
+ del self.instances[account]['online_dialog'][name]
+ for request in self.gpg_passphrase.values():
+ if request:
+ request.interrupt()
+ # .keys() is needed because dict changes during loop
+ for account in self.pass_dialog.keys():
+ self.pass_dialog[account].window.destroy()
+ if show == 'offline':
+ # sensitivity for this menuitem
+ if gajim.get_number_of_connected_accounts() == 0:
+ model[self.roster.status_message_menuitem_iter][3] = False
+ gajim.block_signed_in_notifications[account] = True
+ else:
+ # 30 seconds after we change our status to sth else than offline
+ # 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)
+ # sensitivity for this menuitem
+ model[self.roster.status_message_menuitem_iter][3] = True
+
+ # Inform all controls for this account of the connection state change
+ ctrls = self.msg_win_mgr.get_controls()
+ if account in self.minimized_controls:
+ # Can not be the case when we remove account
+ ctrls += self.minimized_controls[account].values()
+ for ctrl in ctrls:
+ if ctrl.account == account:
+ if show == 'offline' or (show == 'invisible' and \
+ gajim.connections[account].is_zeroconf):
+ ctrl.got_disconnected()
+ else:
+ # Other code rejoins all GCs, so we don't do it here
+ if not ctrl.type_id == message_control.TYPE_GC:
+ ctrl.got_connected()
+ if ctrl.parent_win:
+ ctrl.parent_win.redraw_tab(ctrl)
+
+ self.roster.on_status_changed(account, show)
+ if account in self.show_vcard_when_connect and show not in ('offline',
+ 'error'):
+ self.edit_own_details(account)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('AccountPresence', (show, account))
+
+ def edit_own_details(self, account):
+ jid = gajim.get_jid_from_account(account)
+ if 'profile' not in self.instances[account]:
+ self.instances[account]['profile'] = \
+ profile_window.ProfileWindow(account)
+ gajim.connections[account].request_vcard(jid)
+
+ def handle_event_notify(self, account, array):
+ # 'NOTIFY' (account, (jid, status, status message, resource,
+ # priority, # keyID, timestamp, contact_nickname))
+ #
+ # Contact changed show
+
+ # FIXME: Drop and rewrite...
+
+ statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
+ 'invisible']
+ # Ignore invalid show
+ if array[1] not in statuss:
+ return
+ old_show = 0
+ new_show = statuss.index(array[1])
+ status_message = array[2]
+ jid = array[0].split('/')[0]
+ keyID = array[5]
+ contact_nickname = array[7]
+
+ # Get the proper keyID
+ keyID = helpers.prepare_and_validate_gpg_keyID(account, jid, keyID)
+
+ resource = array[3]
+ if not resource:
+ resource = ''
+ priority = array[4]
+ if gajim.jid_is_transport(jid):
+ # It must be an agent
+ ji = jid.replace('@', '')
+ else:
+ ji = jid
+
+ highest = gajim.contacts. \
+ get_contact_with_highest_priority(account, jid)
+ was_highest = (highest and highest.resource == resource)
+
+ conn = gajim.connections[account]
+
+ # Update contact
+ jid_list = gajim.contacts.get_jid_list(account)
+ if ji in jid_list or jid == gajim.get_jid_from_account(account):
+ lcontact = gajim.contacts.get_contacts(account, ji)
+ contact1 = None
+ resources = []
+ for c in lcontact:
+ resources.append(c.resource)
+ if c.resource == resource:
+ contact1 = c
+ break
+
+ if contact1:
+ if contact1.show in statuss:
+ old_show = statuss.index(contact1.show)
+ # nick changed
+ if contact_nickname is not None and \
+ contact1.contact_name != contact_nickname:
+ 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
+ return
+ else:
+ contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
+ if not contact1:
+ # Presence of another resource of our
+ # jid
+ # Create self contact and add to roster
+ if resource == conn.server_resource:
+ return
+ # Ignore offline presence of unknown self resource
+ if new_show < 2:
+ return
+ contact1 = gajim.contacts.create_contact(jid=ji,
+ name=gajim.nicks[account], groups=['self_contact'],
+ show=array[1], status=status_message, sub='both', ask='none',
+ priority=priority, keyID=keyID, resource=resource,
+ mood=conn.mood, tune=conn.tune, activity=conn.activity)
+ old_show = 0
+ gajim.contacts.add_contact(account, contact1)
+ lcontact.append(contact1)
+ elif contact1.show in statuss:
+ old_show = statuss.index(contact1.show)
+ if (resources != [''] and (len(lcontact) != 1 or \
+ lcontact[0].show != 'offline')) and jid.find('@') > 0:
+ # Another resource of an existing contact connected
+ old_show = 0
+ contact1 = gajim.contacts.copy_contact(contact1)
+ lcontact.append(contact1)
+ contact1.resource = resource
+
+ self.roster.add_contact(contact1.jid, account)
+
+ if contact1.jid.find('@') > 0 and len(lcontact) == 1:
+ # It's not an agent
+ if old_show == 0 and new_show > 1:
+ if not contact1.jid in gajim.newly_added[account]:
+ 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)
+ 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)
+
+ # unset custom status
+ 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]
+
+ contact1.show = array[1]
+ contact1.status = status_message
+ contact1.priority = priority
+ contact1.keyID = keyID
+ timestamp = array[6]
+ if timestamp:
+ contact1.last_status_time = timestamp
+ elif not gajim.block_signed_in_notifications[account]:
+ # We're connected since more that 30 seconds
+ contact1.last_status_time = time.localtime()
+ contact1.contact_nickname = contact_nickname
+
+ if gajim.jid_is_transport(jid):
+ # It must be an agent
+ if ji in jid_list:
+ # Update existing iter and group counting
+ self.roster.draw_contact(ji, account)
+ self.roster.draw_group(_('Transports'), account)
+ if new_show > 1 and ji in gajim.transport_avatar[account]:
+ # transport just signed in.
+ # request avatars
+ for jid_ in gajim.transport_avatar[account][ji]:
+ conn.request_vcard(jid_)
+ # transport just signed in/out, don't show
+ # popup notifications for 30s
+ account_ji = account + '/' + ji
+ gajim.block_signed_in_notifications[account_ji] = True
+ gobject.timeout_add_seconds(30,
+ self.unblock_signed_in_notifications, account_ji)
+ locations = (self.instances, self.instances[account])
+ for location in locations:
+ if 'add_contact' in location:
+ if old_show == 0 and new_show > 1:
+ location['add_contact'].transport_signed_in(jid)
+ break
+ elif old_show > 1 and new_show == 0:
+ location['add_contact'].transport_signed_out(jid)
+ break
+ elif ji in jid_list:
+ # It isn't an agent
+ # reset chatstate if needed:
+ # (when contact signs out or has errors)
+ if array[1] in ('offline', 'error'):
+ contact1.our_chatstate = contact1.chatstate = \
+ contact1.composing_xep = None
+
+ # TODO: This causes problems when another
+ # resource signs off!
+ conn.remove_transfers_for_contact(contact1)
+
+ # disable encryption, since if any messages are
+ # lost they'll be not decryptable (note that
+ # this contradicts XEP-0201 - trying to get that
+ # in the XEP, though)
+
+ # 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):
+ continue
+ if sess.control:
+ sess.control.no_autonegotiation = False
+ if sess.enable_encryption:
+ sess.terminate_e2e()
+ conn.delete_session(jid, sess.thread_id)
+
+ self.roster.chg_contact_status(contact1, array[1], status_message,
+ account)
+ # Notifications
+ if old_show < 2 and new_show > 1:
+ notify.notify('contact_connected', jid, account, status_message)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('ContactPresence', (account,
+ array))
+
+ elif old_show > 1 and new_show < 2:
+ notify.notify('contact_disconnected', jid, account, status_message)
+ if self.remote_ctrl:
+ 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])
+ if self.remote_ctrl:
+ 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))
+
+ 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
+ if (was_highest and not is_highest) or (not was_highest and is_highest):
+ ctrl = self.msg_win_mgr.get_control(jid, account)
+
+ if ctrl:
+ ctrl.no_autonegotiation = False
+ ctrl.set_session(None)
+ ctrl.contact = highest
+
+ def handle_event_msgerror(self, account, array):
+ #'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]
+
+ if array[1] == '503':
+ # If we get server-not-found error, stop sending chatstates
+ for contact in gajim.contacts.get_contacts(account, jid):
+ contact.composing_xep = False
+
+ session = None
+ if len(array) > 5:
+ session = array[5]
+
+ gc_control = self.msg_win_mgr.get_gc_control(jid, account)
+ if not gc_control and \
+ jid in self.minimized_controls[account]:
+ gc_control = self.minimized_controls[account][jid]
+ if gc_control and gc_control.type_id != message_control.TYPE_GC:
+ gc_control = None
+ if gc_control:
+ if len(jids) > 1: # it's a pm
+ nick = jids[1]
+
+ if session:
+ ctrl = session.control
+ else:
+ ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account)
+
+ if not ctrl:
+ tv = gc_control.list_treeview
+ model = tv.get_model()
+ iter_ = gc_control.get_contact_iter(nick)
+ if iter_:
+ show = model[iter_][3]
+ else:
+ show = 'offline'
+ gc_c = gajim.contacts.create_gc_contact(room_jid = jid,
+ 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')
+ 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:
+ gc_control.set_subject(gc_control.subject)
+ return
+
+ if gajim.jid_is_transport(jid):
+ jid = jid.replace('@', '')
+ msg = array[2]
+ if array[3]:
+ msg = _('error while sending %(message)s ( %(error)s )') % {
+ 'message': array[3], 'error': msg}
+ if session:
+ session.roster_message(jid, msg, array[4], msg_type='error')
+
+ def handle_event_msgsent(self, account, array):
+ #('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'):
+ helpers.play_sound('message_sent')
+
+ def handle_event_msgnotsent(self, account, array):
+ #('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
+ 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
+ log.warn(msg)
+ return
+ array[4].roster_message(array[0], msg, array[3], account,
+ msg_type='error')
+
+ def handle_event_subscribe(self, account, array):
+ #('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('Subscribe', (account, array))
+
+ jid = array[0]
+ text = array[1]
+ nick = array[2]
+ if helpers.allow_popup_window(account) or not self.systray_enabled:
+ dialogs.SubscriptionRequestWindow(jid, text, account, nick)
+ return
+
+ self.add_event(account, jid, 'subscription_request', (text, nick))
+
+ if helpers.allow_showing_notification(account):
+ path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'subscription_request.png')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
+ event_type = _('Subscription request')
+ notify.popup(event_type, jid, account, 'subscription_request', path,
+ event_type, jid)
+
+ def handle_event_subscribed(self, account, array):
+ #('SUBSCRIBED', account, (jid, resource))
+ jid = array[0]
+ if jid in gajim.contacts.get_jid_list(account):
+ 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)
+ else:
+ keyID = ''
+ attached_keys = gajim.config.get_per('accounts', account,
+ '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, 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'):
+ gajim.connections[account].ack_subscribed(jid)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('Subscribed', (account, array))
+
+ def show_unsubscribed_dialog(self, account, contact):
+ def on_yes(is_checked, list_):
+ self.roster.on_req_usub(None, list_)
+ list_ = [(contact, account)]
+ dialogs.YesNoDialog(
+ _('Contact "%s" removed subscription from you') % contact.jid,
+ _('You will always see him or her as offline.\nDo you want to '
+ 'remove him or her from your contact list?'),
+ on_response_yes=(on_yes, list_))
+ # FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does
+ # not show deny
+
+ def handle_event_unsubscribed(self, account, jid):
+ #('UNSUBSCRIBED', account, jid)
+ gajim.connections[account].ack_unsubscribed(jid)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('Unsubscribed', (account, jid))
+
+ contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+ if not contact:
+ return
+
+ if helpers.allow_popup_window(account) or not self.systray_enabled:
+ self.show_unsubscribed_dialog(account, contact)
+
+ self.add_event(account, jid, 'unsubscribed', contact)
+
+ if helpers.allow_showing_notification(account):
+ path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'unsubscribed.png')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
+ event_type = _('Unsubscribed')
+ notify.popup(event_type, jid, account, 'unsubscribed', path,
+ event_type, jid)
+
+ def handle_event_agent_info_error(self, account, agent):
+ #('AGENT_ERROR_INFO', account, (agent))
+ try:
+ gajim.connections[account].services_cache.agent_info_error(agent)
+ except AttributeError:
+ return
+
+ def handle_event_agent_items_error(self, account, agent):
+ #('AGENT_ERROR_INFO', account, (agent))
+ try:
+ gajim.connections[account].services_cache.agent_items_error(agent)
+ except AttributeError:
+ return
+
+ def handle_event_agent_removed(self, account, agent):
+ # remove transport's contacts from treeview
+ jid_list = gajim.contacts.get_jid_list(account)
+ for jid in jid_list:
+ if jid.endswith('@' + agent):
+ c = gajim.contacts.get_first_contact_from_jid(account, jid)
+ gajim.log.debug(
+ 'Removing contact %s due to unregistered transport %s'\
+ % (jid, agent))
+ gajim.connections[account].unsubscribe(c.jid)
+ # Transport contacts can't have 2 resources
+ if c.jid in gajim.to_be_removed[account]:
+ # This way we'll really remove it
+ gajim.to_be_removed[account].remove(c.jid)
+ self.roster.remove_contact(c.jid, account, backend=True)
+
+ def handle_event_register_agent_info(self, account, array):
+ # ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
+ # info in a dataform if is_form is True
+ if array[2] or 'instructions' in array[1]:
+ config.ServiceRegistrationWindow(array[0], array[1], account,
+ array[2])
+ else:
+ dialogs.ErrorDialog(_('Contact with "%s" cannot be established') \
+ % array[0], _('Check your connection or try again later.'))
+
+ def handle_event_agent_info_items(self, account, array):
+ #('AGENT_INFO_ITEMS', account, (agent, node, items))
+ our_jid = gajim.get_jid_from_account(account)
+ if 'pep_services' in gajim.interface.instances[account] and \
+ array[0] == our_jid:
+ gajim.interface.instances[account]['pep_services'].items_received(
+ array[2])
+ try:
+ gajim.connections[account].services_cache.agent_items(array[0],
+ array[1], array[2])
+ except AttributeError:
+ return
+
+ def handle_event_agent_info_info(self, account, array):
+ #('AGENT_INFO_INFO', account, (agent, node, identities, features, data))
+ try:
+ gajim.connections[account].services_cache.agent_info(array[0],
+ array[1], array[2], array[3], array[4])
+ except AttributeError:
+ return
+
+ def handle_event_new_acc_connected(self, account, array):
+ #('NEW_ACC_CONNECTED', account, (infos, is_form, ssl_msg, ssl_err,
+ # ssl_cert, ssl_fingerprint))
+ if 'account_creation_wizard' in self.instances:
+ self.instances['account_creation_wizard'].new_acc_connected(array[0],
+ array[1], array[2], array[3], array[4], array[5])
+
+ def handle_event_new_acc_not_connected(self, account, array):
+ #('NEW_ACC_NOT_CONNECTED', account, (reason))
+ if 'account_creation_wizard' in self.instances:
+ self.instances['account_creation_wizard'].new_acc_not_connected(array)
+
+ def handle_event_acc_ok(self, account, array):
+ #('ACC_OK', account, (config))
+ if 'account_creation_wizard' in self.instances:
+ self.instances['account_creation_wizard'].acc_is_ok(array)
+
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('NewAccount', (account, array))
+
+ def handle_event_acc_not_ok(self, account, array):
+ #('ACC_NOT_OK', account, (reason))
+ if 'account_creation_wizard' in self.instances:
+ self.instances['account_creation_wizard'].acc_is_not_ok(array)
+
+ def handle_event_quit(self, p1, p2):
+ self.roster.quit_gtkgui_interface()
+
+ def handle_event_myvcard(self, account, array):
+ nick = ''
+ if 'NICKNAME' in array and array['NICKNAME']:
+ gajim.nicks[account] = array['NICKNAME']
+ elif 'FN' in array and array['FN']:
+ gajim.nicks[account] = array['FN']
+ if 'profile' in self.instances[account]:
+ win = self.instances[account]['profile']
+ win.set_values(array)
+ if account in self.show_vcard_when_connect:
+ self.show_vcard_when_connect.remove(account)
+ jid = array['jid']
+ if jid in self.instances[account]['infos']:
+ self.instances[account]['infos'][jid].set_values(array)
+
+ def handle_event_vcard(self, account, vcard):
+ # ('VCARD', account, data)
+ '''vcard holds the vcard data'''
+ jid = vcard['jid']
+ resource = vcard.get('resource', '')
+ fjid = jid + '/' + str(resource)
+
+ # vcard window
+ win = None
+ if jid in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][jid]
+ elif resource and fjid in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][fjid]
+ if win:
+ win.set_values(vcard)
+
+ # show avatar in chat
+ ctrl = None
+ if resource and self.msg_win_mgr.has_window(fjid, account):
+ win = self.msg_win_mgr.get_window(fjid, account)
+ ctrl = win.get_control(fjid, account)
+ elif self.msg_win_mgr.has_window(jid, account):
+ win = self.msg_win_mgr.get_window(jid, account)
+ ctrl = win.get_control(jid, account)
+
+ if ctrl and ctrl.type_id != message_control.TYPE_GC:
+ ctrl.show_avatar()
+
+ # Show avatar in roster or gc_roster
+ gc_ctrl = self.msg_win_mgr.get_gc_control(jid, account)
+ if not gc_ctrl and \
+ jid in self.minimized_controls[account]:
+ gc_ctrl = self.minimized_controls[account][jid]
+ if gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC:
+ gc_ctrl.draw_avatar(resource)
+ else:
+ self.roster.draw_avatar(jid, account)
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('VcardInfo', (account, vcard))
+
+ def handle_event_last_status_time(self, account, array):
+ # ('LAST_STATUS_TIME', account, (jid, resource, seconds, status))
+ tim = array[2]
+ if tim < 0:
+ # Ann error occured
+ return
+ win = None
+ if array[0] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0]]
+ elif array[0] + '/' + array[1] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0] + '/' + array[1]]
+ c = gajim.contacts.get_contact(account, array[0], array[1])
+ if c: # c can be none if it's a gc contact
+ c.last_status_time = time.localtime(time.time() - tim)
+ if array[3]:
+ c.status = array[3]
+ self.roster.draw_contact(c.jid, account) # draw offline status
+ if win:
+ win.set_last_status_time()
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('LastStatusTime', (account, array))
+
+ def handle_event_os_info(self, account, array):
+ #'OS_INFO' (account, (jid, resource, client_info, os_info))
+ win = None
+ if array[0] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0]]
+ elif array[0] + '/' + array[1] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0] + '/' + array[1]]
+ if win:
+ win.set_os_info(array[1], array[2], array[3])
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('OsInfo', (account, array))
+
+ def handle_event_entity_time(self, account, array):
+ #'ENTITY_TIME' (account, (jid, resource, time_info))
+ win = None
+ if array[0] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0]]
+ elif array[0] + '/' + array[1] in self.instances[account]['infos']:
+ win = self.instances[account]['infos'][array[0] + '/' + array[1]]
+ if win:
+ win.set_entity_time(array[1], array[2])
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('EntityTime', (account, array))
+
+ 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))
+ nick = array[3]
+ if not nick:
+ return
+ room_jid = array[0]
+ fjid = room_jid + '/' + nick
+ show = array[1]
+ status = array[2]
+ conn = gajim.connections[account]
+
+ # Get the window and control for the updated status, this may be a
+ # PrivateChatControl
+ control = self.msg_win_mgr.get_gc_control(room_jid, account)
+
+ if not control and \
+ 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):
+ return
+
+ control.chg_contact_status(nick, show, status, array[4], array[5],
+ array[6], array[7], array[8], array[9], array[10], array[11])
+
+ contact = gajim.contacts.\
+ get_contact_with_highest_priority(account, room_jid)
+ if contact:
+ self.roster.draw_contact(room_jid, account)
+
+ # print status in chat window and update status/GPG image
+ ctrl = self.msg_win_mgr.get_control(fjid, account)
+ if ctrl:
+ 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)
+ c = gajim.contacts.contact_from_gc_contact(gc_c)
+ ctrl.gc_contact = gc_c
+ ctrl.contact = c
+ if ctrl.session:
+ # stop e2e
+ if ctrl.session.enable_encryption:
+ thread_id = ctrl.session.thread_id
+ ctrl.session.terminate_e2e()
+ conn.delete_session(fjid, thread_id)
+ ctrl.no_autonegotiation = False
+ ctrl.draw_banner()
+ old_jid = room_jid + '/' + nick
+ new_jid = room_jid + '/' + new_nick
+ self.msg_win_mgr.change_key(old_jid, new_jid, account)
+ else:
+ contact = ctrl.contact
+ contact.show = show
+ contact.status = status
+ gc_contact = ctrl.gc_contact
+ gc_contact.show = show
+ gc_contact.status = status
+ uf_show = helpers.get_uf_show(show)
+ ctrl.print_conversation(_('%(nick)s is now %(status)s') % {
+ '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(')', 'status', simple=True)
+ ctrl.parent_win.redraw_tab(ctrl)
+ ctrl.update_ui()
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('GCPresence', (account, array))
+
+ def handle_event_gc_msg(self, account, array):
+ # ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg,
+ # [status_codes]))
+ jids = array[0].split('/', 1)
+ room_jid = jids[0]
+
+ msg = array[1]
+
+ gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
+ if not gc_control and \
+ room_jid in self.minimized_controls[account]:
+ gc_control = self.minimized_controls[account][room_jid]
+
+ if not gc_control:
+ return
+ xhtml = array[4]
+
+ if gajim.config.get('ignore_incoming_xhtml'):
+ xhtml = None
+ if len(jids) == 1:
+ # message from server
+ nick = ''
+ else:
+ # message from someone
+ nick = jids[1]
+
+ gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5])
+
+ if self.remote_ctrl:
+ highlight = gc_control.needs_visual_notification(msg)
+ array += (highlight,)
+ self.remote_ctrl.raise_signal('GCMessage', (account, array))
+
+ def handle_event_gc_subject(self, account, array):
+ #('GC_SUBJECT', account, (jid, subject, body, has_timestamp))
+ jids = array[0].split('/', 1)
+ jid = jids[0]
+
+ gc_control = self.msg_win_mgr.get_gc_control(jid, account)
+
+ if not gc_control and \
+ jid in self.minimized_controls[account]:
+ gc_control = self.minimized_controls[account][jid]
+
+ contact = gajim.contacts.\
+ get_contact_with_highest_priority(account, jid)
+ if contact:
+ contact.status = array[1]
+ self.roster.draw_contact(jid, account)
+
+ if not gc_control:
+ return
+ gc_control.set_subject(array[1])
+ # Standard way, the message comes from the occupant who set the subject
+ text = None
+ if len(jids) > 1:
+ text = _('%(jid)s has set the subject to %(subject)s') % {
+ 'jid': jids[1], 'subject': array[1]}
+ # Workaround for psi bug http://flyspray.psi-im.org/task/595 , to be
+ # deleted one day. We can receive a subject with a body that contains
+ # "X has set the subject to Y" ...
+ elif array[2]:
+ text = array[2]
+ if text is not None:
+ if array[3]:
+ gc_control.print_old_conversation(text)
+ else:
+ gc_control.print_conversation(text)
+
+ def handle_event_gc_config(self, account, array):
+ #('GC_CONFIG', account, (jid, form)) config is a dict
+ room_jid = array[0].split('/')[0]
+ if room_jid in gajim.automatic_rooms[account]:
+ if 'continue_tag' in gajim.automatic_rooms[account][room_jid]:
+ # We're converting chat to muc. allow participants to invite
+ form = dataforms.ExtendForm(node = array[1])
+ for f in form.iter_fields():
+ if f.var == 'muc#roomconfig_allowinvites':
+ f.value = True
+ elif f.var == 'muc#roomconfig_publicroom':
+ f.value = False
+ elif f.var == 'muc#roomconfig_membersonly':
+ f.value = True
+ elif f.var == 'public_list':
+ f.value = False
+ gajim.connections[account].send_gc_config(room_jid, form)
+ else:
+ # use default configuration
+ gajim.connections[account].send_gc_config(room_jid, array[1])
+ # invite contacts
+ # check if it is necessary to add
+ continue_tag = False
+ if 'continue_tag' in gajim.automatic_rooms[account][room_jid]:
+ continue_tag = True
+ if 'invities' in gajim.automatic_rooms[account][room_jid]:
+ for jid in gajim.automatic_rooms[account][room_jid]['invities']:
+ gajim.connections[account].send_invite(room_jid, jid,
+ continue_tag=continue_tag)
+ del gajim.automatic_rooms[account][room_jid]
+ elif room_jid not in self.instances[account]['gc_config']:
+ self.instances[account]['gc_config'][room_jid] = \
+ config.GroupchatConfigWindow(account, room_jid, array[1])
+
+ 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
+ jid = array[0]
+ statusCode = array[1]
+
+ gc_control = self.msg_win_mgr.get_gc_control(jid, account)
+ if not gc_control and \
+ jid in self.minimized_controls[account]:
+ gc_control = self.minimized_controls[account][jid]
+ if not gc_control:
+ return
+
+ changes = []
+ if '100' in statusCode:
+ # Can be a presence (see chg_contact_status in groupchat_control.py)
+ changes.append(_('Any occupant is allowed to see your full JID'))
+ gc_control.is_anonymous = False
+ if '102' in statusCode:
+ changes.append(_('Room now shows unavailable member'))
+ 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'))
+ if '170' in statusCode:
+ # Can be a presence (see chg_contact_status in groupchat_control.py)
+ changes.append(_('Room logging is now enabled'))
+ if '171' in statusCode:
+ changes.append(_('Room logging is now disabled'))
+ if '172' in statusCode:
+ changes.append(_('Room is now non-anonymous'))
+ gc_control.is_anonymous = False
+ if '173' in statusCode:
+ changes.append(_('Room is now semi-anonymous'))
+ gc_control.is_anonymous = True
+ if '174' in statusCode:
+ changes.append(_('Room is now fully-anonymous'))
+ gc_control.is_anonymous = True
+
+ for change in changes:
+ gc_control.print_conversation(change)
+
+ def handle_event_gc_affiliation(self, account, array):
+ #('GC_AFFILIATION', account, (room_jid, users_dict))
+ room_jid = array[0]
+ if room_jid in self.instances[account]['gc_config']:
+ self.instances[account]['gc_config'][room_jid].\
+ affiliation_list_received(array[1])
+
+ def handle_event_gc_password_required(self, account, array):
+ #('GC_PASSWORD_REQUIRED', account, (room_jid, nick))
+ room_jid = array[0]
+ nick = array[1]
+
+ def on_ok(text):
+ gajim.connections[account].join_gc(nick, room_jid, text)
+ gajim.gc_passwords[room_jid] = text
+
+ def on_cancel():
+ # get and destroy window
+ if room_jid in gajim.interface.minimized_controls[account]:
+ self.roster.on_disconnect(None, room_jid, account)
+ else:
+ win = self.msg_win_mgr.get_window(room_jid, account)
+ ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
+ 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)
+ dlg.input_entry.set_visibility(False)
+
+ def handle_event_gc_invitation(self, account, array):
+ #('GC_INVITATION', (room_jid, jid_from, reason, password, is_continued))
+ jid = gajim.get_jid_without_resource(array[1])
+ room_jid = array[0]
+ if helpers.allow_popup_window(account) or not self.systray_enabled:
+ dialogs.InvitationReceivedDialog(account, room_jid, jid, array[3],
+ array[2], is_continued=array[4])
+ return
+
+ self.add_event(account, jid, 'gc-invitation', (room_jid, array[2],
+ array[3], array[4]))
+
+ if helpers.allow_showing_notification(account):
+ path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'gc_invitation.png')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
+ event_type = _('Groupchat Invitation')
+ notify.popup(event_type, jid, account, 'gc-invitation', path,
+ event_type, room_jid)
+
+ def forget_gpg_passphrase(self, keyid):
+ if keyid in self.gpg_passphrase:
+ del self.gpg_passphrase[keyid]
+ return False
+
+ def handle_event_bad_passphrase(self, account, array):
+ #('BAD_PASSPHRASE', account, ())
+ 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.')
+ dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
+ else:
+ path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'warning.png')
+ notify.popup('warning', account, account, 'warning', path,
+ _('OpenGPG Passphrase Incorrect'),
+ _('You are currently connected without your OpenPGP key.'))
+ keyID = gajim.config.get_per('accounts', account, 'keyid')
+ self.forget_gpg_passphrase(keyID)
+
+ def handle_event_gpg_password_required(self, account, array):
+ #('GPG_PASSWORD_REQUIRED', account, (callback,))
+ callback = array[0]
+ keyid = gajim.config.get_per('accounts', account, 'keyid')
+ if keyid in self.gpg_passphrase:
+ request = self.gpg_passphrase[keyid]
+ else:
+ request = PassphraseRequest(keyid)
+ self.gpg_passphrase[keyid] = request
+ request.add_callback(account, callback)
+
+ def handle_event_gpg_always_trust(self, account, callback):
+ #('GPG_ALWAYS_TRUST', account, callback)
+ def on_yes(checked):
+ if checked:
+ gajim.connections[account].gpg.always_trust = True
+ callback(True)
+
+ def on_no():
+ 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)
+
+ def handle_event_password_required(self, account, array):
+ #('PASSWORD_REQUIRED', account, None)
+ if account in self.pass_dialog:
+ return
+ text = _('Enter your password for account %s') % account
+ 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)')
+
+ def on_ok(passphrase, save):
+ if save:
+ gajim.config.set_per('accounts', account, 'savepass', True)
+ passwords.save_password(account, passphrase)
+ gajim.connections[account].set_password(passphrase)
+ del self.pass_dialog[account]
+
+ def on_cancel():
+ self.roster.set_state(account, 'offline')
+ self.roster.update_status_combobox()
+ del self.pass_dialog[account]
+
+ self.pass_dialog[account] = dialogs.PassphraseDialog(
+ _('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))
+ jid = array[0]
+ name = array[1]
+ sub = array[2]
+ ask = array[3]
+ groups = array[4]
+ contacts = gajim.contacts.get_contacts(account, jid)
+ if (not sub or sub == 'none') and (not ask or ask == 'none') and \
+ not name and not groups:
+ # contact removed us.
+ if contacts:
+ self.roster.remove_contact(jid, account, backend=True)
+ return
+ elif not contacts:
+ if sub == 'remove':
+ return
+ # Add new contact to roster
+ contact = gajim.contacts.create_contact(jid=jid, name=name,
+ groups=groups, show='offline', sub=sub, ask=ask)
+ 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
+ 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
+ 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)
+
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('RosterInfo', (account, array))
+
+ def handle_event_bookmarks(self, account, bms):
+ # ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
+ # We received a bookmark item from the server (JEP48)
+ # Auto join GC windows if neccessary
+
+ self.roster.set_actions_menu_needs_rebuild()
+ invisible_show = gajim.SHOW_LIST.index('invisible')
+ # do not autojoin if we are invisible
+ if gajim.connections[account].connected == invisible_show:
+ return
+
+ self.auto_join_bookmarks(account)
+
+ def handle_event_file_send_error(self, account, array):
+ jid = array[0]
+ file_props = array[1]
+ ft = self.instances['file_transfers']
+ ft.set_status(file_props['type'], file_props['sid'], 'stop')
+
+ if helpers.allow_popup_window(account):
+ ft.show_send_error(file_props)
+ return
+
+ self.add_event(account, jid, 'file-send-error', file_props)
+
+ if helpers.allow_showing_notification(account):
+ img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
+ event_type = _('File Transfer Error')
+ notify.popup(event_type, jid, account, 'file-send-error', path,
+ event_type, file_props['name'])
+
+ def handle_event_gmail_notify(self, account, array):
+ jid = array[0]
+ gmail_new_messages = int(array[1])
+ gmail_messages_list = array[2]
+ if gajim.config.get('notify_on_new_gmail_email'):
+ img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'new_email_recv.png')
+ title = _('New mail on %(gmail_mail_address)s') % \
+ {'gmail_mail_address': jid}
+ text = i18n.ngettext('You have %d new mail conversation',
+ 'You have %d new mail conversations', gmail_new_messages,
+ gmail_new_messages, gmail_new_messages)
+
+ 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:
+ 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']}
+ cnt += 1
+
+ if gajim.config.get_per('soundevents', 'gmail_received', 'enabled'):
+ helpers.play_sound('gmail_received')
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
+ notify.popup(_('New E-mail'), jid, account, 'gmail',
+ path_to_image=path, title=title,
+ text=text)
+
+ if self.remote_ctrl:
+ self.remote_ctrl.raise_signal('NewGmail', (account, array))
+
+ def handle_event_file_request_error(self, account, array):
+ # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
+ jid, file_props, errmsg = array
+ ft = self.instances['file_transfers']
+ ft.set_status(file_props['type'], file_props['sid'], 'stop')
+ errno = file_props['error']
+
+ if helpers.allow_popup_window(account):
+ if errno in (-4, -5):
+ ft.show_stopped(jid, file_props, errmsg)
+ else:
+ ft.show_request_error(file_props)
+ return
+
+ if errno in (-4, -5):
+ msg_type = 'file-error'
+ else:
+ msg_type = 'file-request-error'
+
+ self.add_event(account, jid, msg_type, file_props)
+
+ if helpers.allow_showing_notification(account):
+ # check if we should be notified
+ img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
+
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
+ event_type = _('File Transfer Error')
+ notify.popup(event_type, jid, account, msg_type, path,
+ title = event_type, text = file_props['name'])
+
+ def handle_event_file_request(self, account, array):
+ jid = array[0]
+ if jid not in gajim.contacts.get_jid_list(account):
+ keyID = ''
+ attached_keys = gajim.config.get_per('accounts', account,
+ 'attached_gpg_keys').split()
+ if jid in attached_keys:
+ keyID = attached_keys[attached_keys.index(jid) + 1]
+ contact = gajim.contacts.create_contact(jid=jid, name='',
+ groups=[_('Not in Roster')], show='not in roster', status='',
+ sub='none', keyID=keyID)
+ gajim.contacts.add_contact(account, contact)
+ self.roster.add_contact(contact.jid, account)
+ file_props = array[1]
+ contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+
+ if helpers.allow_popup_window(account):
+ self.instances['file_transfers'].show_file_request(account, contact,
+ file_props)
+ return
+
+ self.add_event(account, jid, 'file-request', file_props)
+
+ if helpers.allow_showing_notification(account):
+ img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
+ 'ft_request.png')
+ txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(
+ account, jid)
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
+ event_type = _('File Transfer Request')
+ notify.popup(event_type, jid, account, 'file-request',
+ path_to_image = path, title = event_type, text = txt)
+
+ def handle_event_file_error(self, title, message):
+ dialogs.ErrorDialog(title, message)
+
+ def handle_event_file_progress(self, account, file_props):
+ if time.time() - self.last_ftwindow_update > 0.5:
+ # update ft window every 500ms
+ self.last_ftwindow_update = time.time()
+ self.instances['file_transfers'].set_progress(file_props['type'],
+ file_props['sid'], file_props['received-len'])
+
+ def handle_event_file_rcv_completed(self, account, file_props):
+ ft = self.instances['file_transfers']
+ if file_props['error'] == 0:
+ ft.set_progress(file_props['type'], file_props['sid'],
+ file_props['received-len'])
+ else:
+ ft.set_status(file_props['type'], file_props['sid'], 'stop')
+ if 'stalled' in file_props and file_props['stalled'] or \
+ 'paused' in file_props and file_props['paused']:
+ return
+ if file_props['type'] == 'r': # we receive a file
+ jid = unicode(file_props['sender'])
+ else: # we send a file
+ jid = unicode(file_props['receiver'])
+
+ if helpers.allow_popup_window(account):
+ if file_props['error'] == 0:
+ if gajim.config.get('notify_on_file_complete'):
+ ft.show_completed(jid, file_props)
+ elif file_props['error'] == -1:
+ 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'))
+ return
+
+ msg_type = ''
+ event_type = ''
+ if file_props['error'] == 0 and gajim.config.get(
+ 'notify_on_file_complete'):
+ msg_type = 'file-completed'
+ event_type = _('File Transfer Completed')
+ elif file_props['error'] in (-1, -6):
+ msg_type = 'file-stopped'
+ event_type = _('File Transfer Stopped')
+
+ 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
+ return
+
+ if msg_type:
+ self.add_event(account, jid, msg_type, file_props)
+
+ if file_props is not None:
+ if file_props['type'] == 'r':
+ # get the name of the sender, as it is in the roster
+ sender = unicode(file_props['sender']).split('/')[0]
+ name = gajim.contacts.get_first_contact_from_jid(account,
+ 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}
+ img = 'ft_done.png'
+ else: # ft stopped
+ txt = _('File transfer of %(filename)s from %(name)s stopped.')\
+ % {'filename': filename, 'name': name}
+ img = 'ft_stopped.png'
+ else:
+ receiver = file_props['receiver']
+ if hasattr(receiver, 'jid'):
+ receiver = receiver.jid
+ receiver = receiver.split('/')[0]
+ # get the name of the contact, as it is in the roster
+ name = gajim.contacts.get_first_contact_from_jid(account,
+ receiver).get_shown_name()
+ filename = os.path.basename(file_props['file-name'])
+ if event_type == _('File Transfer Completed'):
+ txt = _('You successfully sent %(filename)s to %(name)s.')\
+ % {'filename': filename, 'name': name}
+ img = 'ft_done.png'
+ else: # ft stopped
+ txt = _('File transfer of %(filename)s to %(name)s stopped.')\
+ % {'filename': filename, 'name': name}
+ img = 'ft_stopped.png'
+ img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', img)
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
+ else:
+ txt = ''
+
+ if gajim.config.get('notify_on_file_complete') and \
+ (gajim.config.get('autopopupaway') or \
+ gajim.connections[account].connected in (2, 3)):
+ # we want to be notified and we are online/chat or we don't mind
+ # bugged when away/na/busy
+ notify.popup(event_type, jid, account, msg_type, path_to_image = path,
+ title = event_type, text = txt)
+
+ def handle_event_stanza_arrived(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, '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')
+
+ 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():
+ 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)
+
+ def handle_event_vcard_not_published(self, account, array):
+ if 'profile' in self.instances[account]:
+ win = self.instances[account]['profile']
+ win.vcard_not_published()
+
+ 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)
+
+ def handle_event_signed_in(self, account, empty):
+ '''SIGNED_IN event is emitted when we sign in, so handle it'''
+ # block signed in notifications for 30 seconds
+ gajim.block_signed_in_notifications[account] = True
+ self.roster.set_actions_menu_needs_rebuild()
+ self.roster.draw_account(account)
+ state = self.sleeper.getState()
+ connected = gajim.connections[account].connected
+ if gajim.config.get('ask_offline_status_on_connection'):
+ # Ask offline status in 1 minute so w'are sure we got all online
+ # presences
+ gobject.timeout_add_seconds(60, self.ask_offline_status, account)
+ if state != common.sleepy.STATE_UNKNOWN and connected in (2, 3):
+ # we go online or free for chat, so we activate auto status
+ 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
+ # Else disable autoaway
+ gajim.sleeper_state[account] = 'off'
+ invisible_show = gajim.SHOW_LIST.index('invisible')
+ # We cannot join rooms if we are invisible
+ 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():
+ 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]:
+ continue
+ nick = gc_control.nick
+ password = gajim.gc_passwords.get(room_jid, '')
+ gajim.connections[account].join_gc(nick, room_jid, password)
+
+ def handle_event_metacontacts(self, account, tags_list):
+ gajim.contacts.define_metacontacts(account, tags_list)
+ self.roster.redraw_metacontacts(account)
+
+ def handle_atom_entry(self, account, data):
+ atom_entry, = data
+ AtomWindow.newAtomEntry(atom_entry)
+
+ def handle_event_failed_decrypt(self, account, data):
+ jid, tim, session = data
+
+ details = _('Unable to decrypt message from '
+ '%s\nIt may have been tampered with.') % jid
+
+ ctrl = session.control
+ if ctrl:
+ ctrl.print_conversation_line(details, 'status', '', tim)
+ else:
+ dialogs.WarningDialog(_('Unable to decrypt message'),
+ details)
+
+ # terminate the session
+ session.terminate_e2e()
+ session.conn.delete_session(jid, session.thread_id)
+
+ # restart the session
+ if ctrl:
+ ctrl.begin_e2e_negotiation()
+
+ def handle_event_privacy_lists_received(self, account, data):
+ # ('PRIVACY_LISTS_RECEIVED', account, list)
+ if account not in self.instances:
+ return
+ if 'privacy_lists' in self.instances[account]:
+ self.instances[account]['privacy_lists'].privacy_lists_received(data)
+
+ def handle_event_privacy_list_received(self, account, data):
+ # ('PRIVACY_LIST_RECEIVED', account, (name, rules))
+ if account not in self.instances:
+ return
+ name = data[0]
+ rules = data[1]
+ if 'privacy_list_%s' % name in self.instances[account]:
+ self.instances[account]['privacy_list_%s' % name].\
+ privacy_list_received(rules)
+ if name == 'block':
+ gajim.connections[account].blocked_contacts = []
+ gajim.connections[account].blocked_groups = []
+ gajim.connections[account].blocked_list = []
+ gajim.connections[account].blocked_all = False
+ for rule in rules:
+ 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'])
+ elif rule['type'] == 'group' and rule['action'] == 'deny':
+ 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)
+
+ def handle_event_privacy_lists_active_default(self, account, data):
+ if not data:
+ return
+ # Send to all privacy_list_* windows as we can't know which one asked
+ for win in self.instances[account]:
+ if win.startswith('privacy_list_'):
+ self.instances[account][win].check_active_default(data)
+
+ def handle_event_privacy_list_removed(self, account, name):
+ # ('PRIVACY_LISTS_REMOVED', account, name)
+ if account not in self.instances:
+ return
+ if 'privacy_lists' in self.instances[account]:
+ self.instances[account]['privacy_lists'].privacy_list_removed(name)
+
+ def handle_event_zc_name_conflict(self, account, data):
+ def on_ok(new_name):
+ gajim.config.set_per('accounts', account, 'name', new_name)
+ status = gajim.connections[account].status
+ gajim.connections[account].username = new_name
+ gajim.connections[account].change_status(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)
+
+ def handle_event_ping_sent(self, account, contact):
+ if contact.jid == contact.get_full_jid():
+ # If contact is a groupchat user
+ jids = [contact.jid]
+ else:
+ jids = [contact.jid, contact.get_full_jid()]
+ for jid in jids:
+ ctrl = self.msg_win_mgr.get_control(jid, account)
+ if ctrl:
+ ctrl.print_conversation(_('Ping?'), 'status')
+
+ def handle_event_ping_reply(self, account, data):
+ contact = data[0]
+ seconds = data[1]
+ if contact.jid == contact.get_full_jid():
+ # If contact is a groupchat user
+ jids = [contact.jid]
+ else:
+ jids = [contact.jid, contact.get_full_jid()]
+ for jid in jids:
+ ctrl = self.msg_win_mgr.get_control(jid, account)
+ if ctrl:
+ ctrl.print_conversation(_('Pong! (%s s.)') % seconds, 'status')
+
+ def handle_event_ping_error(self, account, contact):
+ if contact.jid == contact.get_full_jid():
+ # If contact is a groupchat user
+ jids = [contact.jid]
+ else:
+ jids = [contact.jid, contact.get_full_jid()]
+ for jid in jids:
+ ctrl = self.msg_win_mgr.get_control(jid, account)
+ if ctrl:
+ ctrl.print_conversation(_('Error.'), 'status')
+
+ def handle_event_search_form(self, account, data):
+ # ('SEARCH_FORM', account, (jid, dataform, is_dataform))
+ if data[0] not in self.instances[account]['search']:
+ return
+ self.instances[account]['search'][data[0]].on_form_arrived(data[1],
+ data[2])
+
+ def handle_event_search_result(self, account, data):
+ # ('SEARCH_RESULT', account, (jid, dataform, is_dataform))
+ if data[0] not in self.instances[account]['search']:
+ return
+ self.instances[account]['search'][data[0]].on_result_arrived(data[1],
+ data[2])
+
+ def handle_event_resource_conflict(self, account, data):
+ # ('RESOURCE_CONFLICT', account, ())
+ # First we go offline, but we don't overwrite status message
+ self.roster.send_status(account, 'offline',
+ 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)
+ dlg = dialogs.InputDialog(_('Resource Conflict'),
+ _('You are already connected to this account with the same resource. Please type a new one'), input_str = gajim.connections[account].server_resource,
+ is_modal = False, ok_handler = on_ok)
+
+ def handle_event_pep_config(self, account, data):
+ # ('PEP_CONFIG', account, (node, form))
+ if 'pep_services' in self.instances[account]:
+ self.instances[account]['pep_services'].config(data[0], data[1])
+
+ def handle_event_roster_item_exchange(self, account, data):
+ # data = (action in [add, delete, modify], exchange_list, jid_from)
+ dialogs.RosterItemExchangeWindow(account, data[0], data[1], data[2])
+
+ def handle_event_unique_room_id_supported(self, account, data):
+ '''Receive confirmation that unique_room_id are supported'''
+ # ('UNIQUE_ROOM_ID_SUPPORTED', server, instance, room_id)
+ instance = data[1]
+ instance.unique_room_id_supported(data[0], data[2])
+
+ def handle_event_unique_room_id_unsupported(self, account, data):
+ # ('UNIQUE_ROOM_ID_UNSUPPORTED', server, instance)
+ instance = data[1]
+ instance.unique_room_id_error(data[0])
+
+ def handle_event_ssl_error(self, account, data):
+ # ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint))
+ server = gajim.config.get_per('accounts', account, 'hostname')
+
+ def on_ok(is_checked):
+ del self.instances[account]['online_dialog']['ssl_error']
+ if is_checked[0]:
+ # Check if cert is already in file
+ certs = ''
+ if os.path.isfile(gajim.MY_CACERTS):
+ f = open(gajim.MY_CACERTS)
+ certs = f.read()
+ 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)
+ 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])
+ if is_checked[1]:
+ ignore_ssl_errors = gajim.config.get_per('accounts', account,
+ 'ignore_ssl_errors').split()
+ ignore_ssl_errors.append(str(data[1]))
+ gajim.config.set_per('accounts', account, 'ignore_ssl_errors',
+ ' '.join(ignore_ssl_errors))
+ gajim.connections[account].ssl_certificate_accepted()
+
+ def on_cancel():
+ del self.instances[account]['online_dialog']['ssl_error']
+ gajim.connections[account].disconnect(on_purpose=True)
+ 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]}
+ 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]
+ 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)
+
+ 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])
+ # Reset the ignored ssl errors
+ gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '')
+ gajim.connections[account].ssl_certificate_accepted()
+ def on_no():
+ del self.instances[account]['online_dialog']['fingerprint_error']
+ gajim.connections[account].disconnect(on_purpose=True)
+ 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]}
+ if 'fingerprint_error' in self.instances[account]['online_dialog']:
+ 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)
+
+ 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()
+ return
+ # On cancel call del self.instances, so don't call it another time
+ # before
+ del self.instances[account]['online_dialog']['plain_connection']
+ if is_checked[1]:
+ gajim.config.set_per('accounts', account,
+ 'warn_when_plaintext_connection', False)
+ gajim.connections[account].connection_accepted(data[0], 'plain')
+ def on_cancel():
+ del self.instances[account]['online_dialog']['plain_connection']
+ 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?')
+ checktext1 = _('Yes, I really want to connect insecurely')
+ 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'] = \
+ dialogs.ConfirmationDialogDubbleCheck(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):
+ del self.instances[account]['online_dialog']['insecure_ssl']
+ if not is_checked[0]:
+ on_cancel()
+ return
+ if is_checked[1]:
+ gajim.config.set_per('accounts', account,
+ 'warn_when_insecure_ssl_connection', 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].connection_accepted(data[0], data[1])
+ def on_cancel():
+ del self.instances[account]['online_dialog']['insecure_ssl']
+ 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 insecure '
+ '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')
+ 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)
+
+ 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 register_handlers(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,
+ '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_ERROR_INFO': self.handle_event_agent_info_error,
+ 'AGENT_ERROR_ITEMS': self.handle_event_agent_items_error,
+ '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,
+ 'AGENT_INFO_INFO': self.handle_event_agent_info_info,
+ 'QUIT': self.handle_event_quit,
+ 'NEW_ACC_CONNECTED': self.handle_event_new_acc_connected,
+ 'NEW_ACC_NOT_CONNECTED': self.handle_event_new_acc_not_connected,
+ 'ACC_OK': self.handle_event_acc_ok,
+ 'ACC_NOT_OK': self.handle_event_acc_not_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,
+ }
+
+ def dispatch(self, event, account, data):
+ '''
+ Dispatches an network event to the event handlers of this class
+ '''
+ if event not in self.handlers:
+ log.warning('Unknown event %s dispatched to GUI: %s' % (event, data))
+ else:
+ log.debug('Event %s distpached to GUI: %s' % (event, data))
+ self.handlers[event](account, data)
+
+
+################################################################################
+### Methods dealing with gajim.events
+################################################################################
+
+ def add_event(self, account, jid, type_, event_args):
+ '''add an event to the gajim.events var'''
+ # We add it to the gajim.events queue
+ # 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
+ # event_type can be in advancedNotificationWindow.events_list
+ event_types = {'file-request': 'ft_request',
+ '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)
+ 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 type_ == 'gc-invitation':
+ self.roster.add_groupchat(jid, account, status='offline')
+ else:
+ # add contact to roster ("Not In The Roster") if he is not
+ self.roster.add_to_not_in_the_roster(account, jid)
+ else:
+ self.roster.draw_contact(jid, account)
+
+ # Select the big brother contact in roster, it's visible because it has
+ # events.
+ family = gajim.contacts.get_metacontacts_family(account, jid)
+ if family:
+ nearby_family, bb_jid, bb_account = \
+ self.roster._get_nearby_family_and_big_brother(family, account)
+ else:
+ bb_jid, bb_account = jid, account
+ self.roster.select_contact(bb_jid, bb_account)
+
+ def handle_event(self, account, fjid, type_):
+ w = None
+ ctrl = None
+ session = None
+
+ resource = gajim.get_resource_from_jid(fjid)
+ jid = gajim.get_jid_without_resource(fjid)
+
+ if type_ in ('printed_gc_msg', 'printed_marked_gc_msg', 'gc_msg'):
+ w = self.msg_win_mgr.get_window(jid, account)
+ if jid in self.minimized_controls[account]:
+ self.roster.on_groupchat_maximized(None, jid, account)
+ return
+ else:
+ ctrl = self.msg_win_mgr.get_gc_control(jid, account)
+
+ elif type_ in ('printed_chat', 'chat', ''):
+ # '' is for log in/out notifications
+
+ if type_ != '':
+ event = gajim.events.get_first_event(account, fjid, type_)
+ if not event:
+ event = gajim.events.get_first_event(account, jid, type_)
+ if not event:
+ return
+
+ if type_ == 'printed_chat':
+ ctrl = event.parameters[0]
+ elif type_ == 'chat':
+ session = event.parameters[8]
+ ctrl = session.control
+ elif type_ == '':
+ ctrl = self.msg_win_mgr.get_control(fjid, account)
+
+ if not ctrl:
+ 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 \
+ not self.msg_win_mgr.has_window(jid, account):
+ # remove resource of events too
+ gajim.events.change_jid(account, fjid, jid)
+ resource = None
+ fjid = jid
+ contact = None
+ if resource:
+ contact = gajim.contacts.get_contact(account, jid, resource)
+ if not contact:
+ contact = highest_contact
+
+ 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
+ event = gajim.events.get_first_event(account, fjid, type_)
+ if not event:
+ event = gajim.events.get_first_event(account, jid, type_)
+
+ if type_ == 'printed_pm':
+ ctrl = event.parameters[0]
+ elif type_ == 'pm':
+ session = event.parameters[8]
+
+ if session and session.control:
+ ctrl = session.control
+ elif not ctrl:
+ room_jid = jid
+ nick = resource
+ gc_contact = gajim.contacts.get_gc_contact(account, room_jid,
+ nick)
+ if gc_contact:
+ show = gc_contact.show
+ else:
+ show = 'offline'
+ gc_contact = gajim.contacts.create_gc_contact(
+ room_jid = room_jid, name = nick, show = show)
+
+ if not session:
+ session = gajim.connections[account].make_new_session(
+ fjid, None, type_='pm')
+
+ self.new_private_chat(gc_contact, account, session=session)
+ ctrl = session.control
+
+ w = ctrl.parent_win
+ elif type_ in ('normal', 'file-request', 'file-request-error',
+ 'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
+ # Get the first single message event
+ event = gajim.events.get_first_event(account, fjid, type_)
+ if not event:
+ # default to jid without resource
+ event = gajim.events.get_first_event(account, jid, type_)
+ if not event:
+ return
+ # Open the window
+ self.roster.open_event(account, jid, event)
+ else:
+ # Open the window
+ self.roster.open_event(account, fjid, event)
+ elif type_ == 'gmail':
+ url=gajim.connections[account].gmail_url
+ if url:
+ helpers.launch_browser_mailer('url', url)
+ elif type_ == 'gc-invitation':
+ event = gajim.events.get_first_event(account, jid, type_)
+ data = event.parameters
+ dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
+ data[1], data[3])
+ gajim.events.remove_events(account, jid, event)
+ self.roster.draw_contact(jid, account)
+ elif type_ == 'subscription_request':
+ event = gajim.events.get_first_event(account, jid, type_)
+ data = event.parameters
+ dialogs.SubscriptionRequestWindow(jid, data[0], account, data[1])
+ gajim.events.remove_events(account, jid, event)
+ self.roster.draw_contact(jid, account)
+ elif type_ == 'unsubscribed':
+ event = gajim.events.get_first_event(account, jid, type_)
+ contact = event.parameters
+ self.show_unsubscribed_dialog(account, contact)
+ gajim.events.remove_events(account, jid, event)
+ self.roster.draw_contact(jid, account)
+ if w:
+ w.set_active_tab(ctrl)
+ w.window.window.focus(gtk.get_current_event_time())
+ # Using isinstance here because we want to catch all derived types
+ if isinstance(ctrl, ChatControlBase):
+ tv = ctrl.conv_textview
+ tv.scroll_to_end()
+
+################################################################################
+### Methods dealing with emoticons
+################################################################################
+
+ def image_is_ok(self, image):
+ if not os.path.exists(image):
+ return False
+ img = gtk.Image()
+ try:
+ img.set_from_file(image)
+ except Exception:
+ return False
+ t = img.get_storage_type()
+ if t != gtk.IMAGE_PIXBUF and t != gtk.IMAGE_ANIMATION:
+ return False
+ return True
+
+ @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
+
+ @property
+ def emot_and_basic_re(self):
+ try:
+ return self._emot_and_basic_re
+ except AttributeError:
+ self._emot_and_basic_re = re.compile(self.emot_and_basic,
+ re.IGNORECASE + re.UNICODE)
+ 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:
+ self._sth_at_sth_dot_sth_re = re.compile(self.sth_at_sth_dot_sth)
+ return self._sth_at_sth_dot_sth_re
+
+ @property
+ def invalid_XML_chars_re(self):
+ try:
+ return self._invalid_XML_chars_re
+ except AttributeError:
+ self._invalid_XML_chars_re = re.compile(self.invalid_XML_chars)
+ return self._invalid_XML_chars_re
+
+ def make_regexps(self):
+ # regexp meta characters are: . ^ $ * + ? { } [ ] \ | ( )
+ # one escapes the metachars with \
+ # \S matches anything but ' ' '\t' '\n' '\r' '\f' and '\v'
+ # \s matches any whitespace character
+ # \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 at the beginning of lines
+ #
+ # * means 0 or more times
+ # + means 1 or more times
+ # ? means 0 or 1 time
+ # | means or
+ # [^*] anything but '*' (inside [] you don't have to escape metachars)
+ # [^\s*] anything but whitespaces and '*'
+ # (? 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})+)"
+ # NOTE: it's ok to catch www.gr such stuff exist!
+
+ #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})+)"
+
+ #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*
+ formatting = r'|(?> 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)
+ sys.exit()
+
+ def save_avatar_files(self, jid, photo, puny_nick = None, local = False):
+ '''Saves an avatar to a separate file, and generate files for dbus notifications. An avatar can be given as a pixmap directly or as an decoded image.'''
+ puny_jid = helpers.sanitize_filename(jid)
+ path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid)
+ if puny_nick:
+ path_to_file = os.path.join(path_to_file, puny_nick)
+ # remove old avatars
+ for typ in ('jpeg', 'png'):
+ if local:
+ path_to_original_file = path_to_file + '_local'+ '.' + typ
+ else:
+ path_to_original_file = path_to_file + '.' + typ
+ if os.path.isfile(path_to_original_file):
+ os.remove(path_to_original_file)
+ if local and photo:
+ pixbuf = photo
+ typ = 'png'
+ extension = '_local.png' # save local avatars as png file
+ else:
+ 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))
+ 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)))
+ # 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
+ 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)))
+ # Generate and save the resized, black and white avatar
+ bwbuf = gtkgui_helpers.get_scaled_pixbuf(
+ 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)))
+
+ def remove_avatar_files(self, jid, puny_nick = None, local = False):
+ '''remove avatar files of a jid'''
+ puny_jid = helpers.sanitize_filename(jid)
+ path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid)
+ if puny_nick:
+ path_to_file = os.path.join(path_to_file, puny_nick)
+ for ext in ('.jpeg', '.png'):
+ if local:
+ ext = '_local' + ext
+ path_to_original_file = path_to_file + ext
+ if os.path.isfile(path_to_file + ext):
+ os.remove(path_to_file + ext)
+ if os.path.isfile(path_to_file + '_notif_size_colored' + ext):
+ os.remove(path_to_file + '_notif_size_colored' + ext)
+ if os.path.isfile(path_to_file + '_notif_size_bw' + ext):
+ os.remove(path_to_file + '_notif_size_bw' + ext)
+
+ def auto_join_bookmarks(self, account):
+ '''autojoin bookmarked GCs that have 'auto join' on for this account'''
+ for bm in gajim.connections[account].bookmarks:
+ if bm['autojoin'] in ('1', 'true'):
+ jid = bm['jid']
+ # Only join non-opened groupchats. Opened one are already
+ # auto-joined on re-connection
+ if not jid in gajim.gc_connected[account]:
+ # we are not already connected
+ minimize = bm['minimize'] in ('1', 'true')
+ gajim.interface.join_gc_room(account, jid, bm['nick'],
+ bm['password'], minimize = minimize)
+ 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.
+ self.roster.add_groupchat(jid, account)
+
+ def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
+ nick):
+ '''add a bookmark for this account, sorted in bookmark list'''
+ bm = {
+ 'name': name,
+ 'jid': jid,
+ 'autojoin': autojoin,
+ 'minimize': minimize,
+ 'password': password,
+ 'nick': nick
+ }
+ place_found = False
+ index = 0
+ # check for duplicate entry and respect alpha order
+ 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'])
+ return
+ if bookmark['name'] > bm['name']:
+ place_found = True
+ break
+ index += 1
+ if place_found:
+ gajim.connections[account].bookmarks.insert(index, bm)
+ else:
+ gajim.connections[account].bookmarks.append(bm)
+ 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.'))
+
+
+ # does JID exist only within a groupchat?
+ def is_pm_contact(self, fjid, account):
+ bare_jid = gajim.get_jid_without_resource(fjid)
+
+ gc_ctrl = self.msg_win_mgr.get_gc_control(bare_jid, account)
+
+ if not gc_ctrl and \
+ bare_jid in self.minimized_controls[account]:
+ gc_ctrl = self.minimized_controls[account][bare_jid]
+
+ return gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC
+
+ def create_ipython_window(self):
+ try:
+ from ipython_view import IPythonView
+ except ImportError:
+ print 'ipython_view not found'
+ return
+ import pango
+
+ if os.name == 'nt':
+ font = 'Lucida Console 9'
+ else:
+ font = 'Luxi Mono 10'
+
+ window = gtk.Window()
+ window.set_size_request(750,550)
+ window.set_resizable(True)
+ sw = gtk.ScrolledWindow()
+ sw.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+ view = IPythonView()
+ view.modify_font(pango.FontDescription(font))
+ view.set_wrap_mode(gtk.WRAP_CHAR)
+ sw.add(view)
+ window.add(sw)
+ window.show_all()
+ def on_delete(win, event):
+ win.hide()
+ return True
+ window.connect('delete_event',on_delete)
+ view.updateNamespace({'gajim': gajim})
+ gajim.ipython_window = window
+
+ def run(self):
+ if self.systray_capabilities and gajim.config.get('trayicon') != 'never':
+ self.show_systray()
+
+ self.roster = roster_window.RosterWindow()
+ for account in gajim.connections:
+ gajim.connections[account].load_roster_from_db()
+
+ # get instances for windows/dialogs that will show_all()/hide()
+ self.instances['file_transfers'] = dialogs.FileTransfersWindow()
+
+ gobject.timeout_add(100, self.autoconnect)
+ timeout, in_seconds = gajim.idlequeue.PROCESS_TIMEOUT
+ if in_seconds:
+ gobject.timeout_add_seconds(timeout, self.process_connections)
+ else:
+ gobject.timeout_add(timeout, self.process_connections)
+ gobject.timeout_add_seconds(gajim.config.get(
+ 'check_idle_every_foo_seconds'), self.read_sleepy)
+
+ # when using libasyncns we need to process resolver in regular intervals
+ 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:
+ import remote_control
+ self.remote_ctrl = remote_control.Remote()
+ except Exception:
+ pass
+ gobject.timeout_add_seconds(5, remote_init)
+
+ for account in gajim.connections:
+ if gajim.config.get_per('accounts', account, 'publish_tune') and \
+ dbus_support.supported:
+ self.enable_music_listener()
+ break
+
+ def __init__(self):
+ gajim.interface = self
+ gajim.thread_interface = ThreadInterface
+ # This is the manager and factory of message windows set by the module
+ self.msg_win_mgr = None
+ self.jabber_state_images = {'16': {}, '32': {}, 'opened': {},
+ 'closed': {}}
+ self.emoticons_menu = None
+ # handler when an emoticon is clicked in emoticons_menu
+ self.emoticon_menuitem_clicked = None
+ self.minimized_controls = {}
+ self.status_sent_to_users = {}
+ self.status_sent_to_groups = {}
+ self.gpg_passphrase = {}
+ self.pass_dialog = {}
+ self.default_colors = {
+ 'inmsgcolor': gajim.config.get('inmsgcolor'),
+ 'outmsgcolor': gajim.config.get('outmsgcolor'),
+ 'statusmsgcolor': gajim.config.get('statusmsgcolor'),
+ 'urlmsgcolor': gajim.config.get('urlmsgcolor'),
+ }
+
+ cfg_was_read = parser.read()
+ gajim.logger.reset_shown_unread_messages()
+ # override logging settings from config (don't take care of '-q' option)
+ if gajim.config.get('verbose'):
+ logging_helpers.set_verbose()
+
+ # Is Gajim default app?
+ if os.name != 'nt' and gajim.config.get('check_if_gajim_is_default'):
+ gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
+
+ for account in gajim.config.get_per('accounts'):
+ if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+ gajim.ZEROCONF_ACC_NAME = account
+ break
+ # Is gnome configured to activate row on single click ?
+ try:
+ import gconf
+ client = gconf.client_get_default()
+ click_policy = client.get_string(
+ '/apps/nautilus/preferences/click_policy')
+ if click_policy == 'single':
+ gajim.single_click = True
+ except Exception:
+ pass
+ # add default status messages if there is not in the config file
+ if len(gajim.config.get_per('statusmsg')) == 0:
+ 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, 'subactivity',
+ 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])
+ #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']
+
+ default = gajim.config.themes_default
+ for theme_name in default:
+ gajim.config.add_per('themes', theme_name)
+ theme = default[theme_name]
+ for o in d:
+ gajim.config.set_per('themes', theme_name, o,
+ theme[d.index(o)])
+
+ if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read:
+ gtkgui_helpers.autodetect_browser_mailer()
+
+ gajim.idlequeue = idlequeue.get_idlequeue()
+ # 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)
+ gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
+ gajim.default_session_type = ChatControlSession
+ self.register_handlers()
+ 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)
+ for account in gajim.config.get_per('accounts'):
+ 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)
+
+ # gtk hooks
+ gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
+ gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
+ gtk.link_button_set_uri_hook(self.on_launch_browser_mailer, 'url')
+
+ self.instances = {}
+
+ 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
+ self.minimized_controls[a] = {}
+ gajim.contacts.add_account(a)
+ gajim.groups[a] = {}
+ gajim.gc_connected[a] = {}
+ gajim.automatic_rooms[a] = {}
+ gajim.newly_added[a] = []
+ gajim.to_be_removed[a] = []
+ gajim.nicks[a] = gajim.config.get_per('accounts', a, 'name')
+ gajim.block_signed_in_notifications[a] = True
+ gajim.sleeper_state[a] = 0
+ gajim.encrypted_chats[a] = []
+ gajim.last_message_time[a] = {}
+ gajim.status_before_autoaway[a] = ''
+ gajim.transport_avatar[a] = {}
+ gajim.gajim_optional_features[a] = []
+ gajim.caps_hash[a] = ''
+
+ helpers.update_optional_features()
+
+ self.remote_ctrl = None
+
+ if gajim.config.get('networkmanager_support') and dbus_support.supported:
+ import network_manager_listener
+
+ # Handle gnome screensaver
+ if dbus_support.supported:
+ def gnome_screensaver_ActiveChanged_cb(active):
+ if not active:
+ for account in gajim.connections:
+ if gajim.sleeper_state[account] == 'autoaway-forced':
+ # 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.sleeper_state[account] = 'online'
+ return
+ if not gajim.config.get('autoaway'):
+ # Don't go auto away if user disabled the option
+ return
+ for account in gajim.connections:
+ if account not in gajim.sleeper_state or \
+ not gajim.sleeper_state[account]:
+ continue
+ if gajim.sleeper_state[account] == 'online':
+ # 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]
+ 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 % {
+ 'status': gajim.status_before_autoaway[account],
+ 'time': gajim.config.get('autoxatime')
+ }
+ self.roster.send_status(account, 'away', auto_message,
+ auto=True)
+ gajim.sleeper_state[account] = 'autoaway-forced'
+
+ try:
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(gnome_screensaver_ActiveChanged_cb,
+ '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)
+
+ gtkgui_helpers.make_jabber_state_images()
+
+ self.systray_enabled = False
+ self.systray_capabilities = False
+
+ if (os.name == 'nt'):
+ import statusicon
+ self.systray = statusicon.StatusIcon()
+ self.systray_capabilities = True
+ else: # use ours, not GTK+ one
+ # [FIXME: remove this when we migrate to 2.10 and we can do
+ # cool tooltips somehow and (not dying to keep) animation]
+ import systray
+ self.systray_capabilities = systray.HAS_SYSTRAY_CAPABILITIES
+ if self.systray_capabilities:
+ self.systray = systray.Systray()
+
+ path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png')
+ pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
+ # set the icon to all windows
+ gtk.window_set_default_icon(pix)
+
+ self.init_emoticons()
+ self.make_regexps()
+
+ # get transports type from DB
+ gajim.transport_type = gajim.logger.get_transports_type()
+
+ # test is dictionnary is present for speller
+ if gajim.config.get('use_speller'):
+ lang = gajim.config.get('speller_language')
+ if not lang:
+ lang = gajim.LANG
+ tv = gtk.TextView()
+ try:
+ import gtkspell
+ spell = gtkspell.Spell(tv, lang)
+ except (ImportError, TypeError, RuntimeError, OSError):
+ dialogs.AspellDictError(lang)
+
+ if gajim.config.get('soundplayer') == '':
+ # only on first time Gajim starts
+ commands = ('aplay', 'play', 'esdplay', 'artsplay', 'ossplay')
+ for command in commands:
+ if helpers.is_in_path(command):
+ if command == 'aplay':
+ command += ' -q'
+ gajim.config.set('soundplayer', command)
+ break
+
+ self.last_ftwindow_update = 0
+
+ self.music_track_changed_signal = None
if __name__ == '__main__':
def sigint_cb(num, stack):
diff --git a/src/interface.py b/src/interface.py
deleted file mode 100644
index 16fc9bb21..000000000
--- a/src/interface.py
+++ /dev/null
@@ -1,3355 +0,0 @@
-# -*- coding:utf-8 -*-
-## src/gajim.py
-##
-## Copyright (C) 2003-2008 Yann Leboulanger
-## Copyright (C) 2004-2005 Vincent Hanquez
-## Copyright (C) 2005 Alex Podaras
-## Norman Rasmussen
-## Stéphan Kochen
-## Copyright (C) 2005-2006 Dimitur Kirov
-## Alex Mauer
-## Copyright (C) 2005-2007 Travis Shirk
-## Nikos Kouremenos
-## Copyright (C) 2006 Junglecow J
-## Stefan Bethge
-## Copyright (C) 2006-2008 Jean-Marie Traissard
-## Copyright (C) 2007 Lukas Petrovicky
-## James Newton
-## Copyright (C) 2007-2008 Brendan Taylor
-## Julien Pivotto
-## Stephan Erb
-## Copyright (C) 2008 Jonathan Schleifer
-##
-## This file is part of Gajim.
-##
-## Gajim is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## Gajim is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see .
-##
-
-import os
-import sys
-import re
-import time
-import math
-
-import gtk
-import gobject
-
-from common import i18n
-from common import gajim
-
-from common import dbus_support
-if dbus_support.supported:
- from music_track_listener import MusicTrackListener
- import dbus
-
-import gtkgui_helpers
-
-
-import dialogs
-import notify
-import message_control
-
-from chat_control import ChatControlBase
-from chat_control import ChatControl
-from groupchat_control import GroupchatControl
-from groupchat_control import PrivateChatControl
-
-from atom_window import AtomWindow
-from session import ChatControlSession
-
-import common.sleepy
-
-from common.xmpp import idlequeue
-from common.zeroconf import connection_zeroconf
-from common import resolver
-from common import proxy65_manager
-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
-import profile_window
-import config
-from threading import Thread
-
-gajimpaths = common.configpaths.gajimpaths
-config_filename = gajimpaths['CONFIG_FILE']
-
-from common import optparser
-parser = optparser.OptionsParser(config_filename)
-
-
-import logging
-log = logging.getLogger('gajim.interface')
-
-class Interface:
-
-################################################################################
-### Methods handling events from connection
-################################################################################
-
- def handle_event_roster(self, account, data):
- #('ROSTER', account, array)
- # FIXME: Those methods depend to highly on each other
- # and the order in which they are called
- self.roster.fill_contacts_and_groups_dicts(data, account)
- self.roster.add_account_contacts(account)
- self.roster.fire_up_unread_messages_events(account)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('Roster', (account, data))
-
- def handle_event_warning(self, unused, data):
- #('WARNING', account, (title_text, section_text))
- dialogs.WarningDialog(data[0], data[1])
-
- def handle_event_error(self, unused, data):
- #('ERROR', account, (title_text, section_text))
- dialogs.ErrorDialog(data[0], data[1])
-
- def handle_event_information(self, unused, data):
- #('INFORMATION', account, (title_text, section_text))
- dialogs.InformationDialog(data[0], data[1])
-
- def handle_event_ask_new_nick(self, account, data):
- #('ASK_NEW_NICK', account, (room_jid,))
- 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
- 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)
- else:
- self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
- 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):
- response(account, iq_obj, 'yes')
-
- sec_msg = _('Do you accept this request?')
- if gajim.get_number_of_connected_accounts() > 1:
- 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'))
-
- def handle_event_error_answer(self, account, array):
- #('ERROR_ANSWER', account, (id, jid_from, errmsg, errcode))
- id_, jid_from, errmsg, errcode = array
- if unicode(errcode) in ('400', '403', '406') and id_:
- # show the error dialog
- ft = self.instances['file_transfers']
- sid = id_
- if len(id_) > 3 and id_[2] == '_':
- sid = id_[3:]
- if sid in ft.files_props['s']:
- file_props = ft.files_props['s'][sid]
- if unicode(errcode) == '400':
- file_props['error'] = -3
- else:
- file_props['error'] = -4
- self.handle_event_file_request_error(account,
- (jid_from, file_props, errmsg))
- conn = gajim.connections[account]
- conn.disconnect_transfer(file_props)
- return
- elif unicode(errcode) == '404':
- conn = gajim.connections[account]
- sid = id_
- if len(id_) > 3 and id_[2] == '_':
- sid = id_[3:]
- if sid in conn.files_props:
- file_props = conn.files_props[sid]
- self.handle_event_file_send_error(account,
- (jid_from, file_props))
- conn.disconnect_transfer(file_props)
- return
-
- ctrl = self.msg_win_mgr.get_control(jid_from, account)
- if ctrl and ctrl.type_id == message_control.TYPE_GC:
- ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
-
- def handle_event_con_type(self, account, con_type):
- # ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'plain'
- gajim.con_types[account] = con_type
- self.roster.draw_account(account)
-
- def handle_event_connection_lost(self, account, array):
- # ('CONNECTION_LOST', account, [title, text])
- path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'connection_lost.png')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
- notify.popup(_('Connection Failed'), account, account,
- 'connection_failed', path, array[0], array[1])
-
- def unblock_signed_in_notifications(self, account):
- gajim.block_signed_in_notifications[account] = False
-
- def handle_event_status(self, account, show): # OUR status
- #('STATUS', account, show)
- 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
- self.instances[account]['online_dialog'][name].destroy()
- del self.instances[account]['online_dialog'][name]
- for request in self.gpg_passphrase.values():
- if request:
- request.interrupt()
- # .keys() is needed because dict changes during loop
- for account in self.pass_dialog.keys():
- self.pass_dialog[account].window.destroy()
- if show == 'offline':
- # sensitivity for this menuitem
- if gajim.get_number_of_connected_accounts() == 0:
- model[self.roster.status_message_menuitem_iter][3] = False
- gajim.block_signed_in_notifications[account] = True
- else:
- # 30 seconds after we change our status to sth else than offline
- # 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)
- # sensitivity for this menuitem
- model[self.roster.status_message_menuitem_iter][3] = True
-
- # Inform all controls for this account of the connection state change
- ctrls = self.msg_win_mgr.get_controls()
- if account in self.minimized_controls:
- # Can not be the case when we remove account
- ctrls += self.minimized_controls[account].values()
- for ctrl in ctrls:
- if ctrl.account == account:
- if show == 'offline' or (show == 'invisible' and \
- gajim.connections[account].is_zeroconf):
- ctrl.got_disconnected()
- else:
- # Other code rejoins all GCs, so we don't do it here
- if not ctrl.type_id == message_control.TYPE_GC:
- ctrl.got_connected()
- if ctrl.parent_win:
- ctrl.parent_win.redraw_tab(ctrl)
-
- self.roster.on_status_changed(account, show)
- if account in self.show_vcard_when_connect and show not in ('offline',
- 'error'):
- self.edit_own_details(account)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('AccountPresence', (show, account))
-
- def edit_own_details(self, account):
- jid = gajim.get_jid_from_account(account)
- if 'profile' not in self.instances[account]:
- self.instances[account]['profile'] = \
- profile_window.ProfileWindow(account)
- gajim.connections[account].request_vcard(jid)
-
- def handle_event_notify(self, account, array):
- # 'NOTIFY' (account, (jid, status, status message, resource,
- # priority, # keyID, timestamp, contact_nickname))
- #
- # Contact changed show
-
- # FIXME: Drop and rewrite...
-
- statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
- 'invisible']
- # Ignore invalid show
- if array[1] not in statuss:
- return
- old_show = 0
- new_show = statuss.index(array[1])
- status_message = array[2]
- jid = array[0].split('/')[0]
- keyID = array[5]
- contact_nickname = array[7]
-
- # Get the proper keyID
- keyID = helpers.prepare_and_validate_gpg_keyID(account, jid, keyID)
-
- resource = array[3]
- if not resource:
- resource = ''
- priority = array[4]
- if gajim.jid_is_transport(jid):
- # It must be an agent
- ji = jid.replace('@', '')
- else:
- ji = jid
-
- highest = gajim.contacts. \
- get_contact_with_highest_priority(account, jid)
- was_highest = (highest and highest.resource == resource)
-
- conn = gajim.connections[account]
-
- # Update contact
- jid_list = gajim.contacts.get_jid_list(account)
- if ji in jid_list or jid == gajim.get_jid_from_account(account):
- lcontact = gajim.contacts.get_contacts(account, ji)
- contact1 = None
- resources = []
- for c in lcontact:
- resources.append(c.resource)
- if c.resource == resource:
- contact1 = c
- break
-
- if contact1:
- if contact1.show in statuss:
- old_show = statuss.index(contact1.show)
- # nick changed
- if contact_nickname is not None and \
- contact1.contact_name != contact_nickname:
- 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
- return
- else:
- contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
- if not contact1:
- # Presence of another resource of our
- # jid
- # Create self contact and add to roster
- if resource == conn.server_resource:
- return
- # Ignore offline presence of unknown self resource
- if new_show < 2:
- return
- contact1 = gajim.contacts.create_contact(jid=ji,
- name=gajim.nicks[account], groups=['self_contact'],
- show=array[1], status=status_message, sub='both', ask='none',
- priority=priority, keyID=keyID, resource=resource,
- mood=conn.mood, tune=conn.tune, activity=conn.activity)
- old_show = 0
- gajim.contacts.add_contact(account, contact1)
- lcontact.append(contact1)
- elif contact1.show in statuss:
- old_show = statuss.index(contact1.show)
- if (resources != [''] and (len(lcontact) != 1 or \
- lcontact[0].show != 'offline')) and jid.find('@') > 0:
- # Another resource of an existing contact connected
- old_show = 0
- contact1 = gajim.contacts.copy_contact(contact1)
- lcontact.append(contact1)
- contact1.resource = resource
-
- self.roster.add_contact(contact1.jid, account)
-
- if contact1.jid.find('@') > 0 and len(lcontact) == 1:
- # It's not an agent
- if old_show == 0 and new_show > 1:
- if not contact1.jid in gajim.newly_added[account]:
- 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)
- 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)
-
- # unset custom status
- 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]
-
- contact1.show = array[1]
- contact1.status = status_message
- contact1.priority = priority
- contact1.keyID = keyID
- timestamp = array[6]
- if timestamp:
- contact1.last_status_time = timestamp
- elif not gajim.block_signed_in_notifications[account]:
- # We're connected since more that 30 seconds
- contact1.last_status_time = time.localtime()
- contact1.contact_nickname = contact_nickname
-
- if gajim.jid_is_transport(jid):
- # It must be an agent
- if ji in jid_list:
- # Update existing iter and group counting
- self.roster.draw_contact(ji, account)
- self.roster.draw_group(_('Transports'), account)
- if new_show > 1 and ji in gajim.transport_avatar[account]:
- # transport just signed in.
- # request avatars
- for jid_ in gajim.transport_avatar[account][ji]:
- conn.request_vcard(jid_)
- # transport just signed in/out, don't show
- # popup notifications for 30s
- account_ji = account + '/' + ji
- gajim.block_signed_in_notifications[account_ji] = True
- gobject.timeout_add_seconds(30,
- self.unblock_signed_in_notifications, account_ji)
- locations = (self.instances, self.instances[account])
- for location in locations:
- if 'add_contact' in location:
- if old_show == 0 and new_show > 1:
- location['add_contact'].transport_signed_in(jid)
- break
- elif old_show > 1 and new_show == 0:
- location['add_contact'].transport_signed_out(jid)
- break
- elif ji in jid_list:
- # It isn't an agent
- # reset chatstate if needed:
- # (when contact signs out or has errors)
- if array[1] in ('offline', 'error'):
- contact1.our_chatstate = contact1.chatstate = \
- contact1.composing_xep = None
-
- # TODO: This causes problems when another
- # resource signs off!
- conn.remove_transfers_for_contact(contact1)
-
- # disable encryption, since if any messages are
- # lost they'll be not decryptable (note that
- # this contradicts XEP-0201 - trying to get that
- # in the XEP, though)
-
- # 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):
- continue
- if sess.control:
- sess.control.no_autonegotiation = False
- if sess.enable_encryption:
- sess.terminate_e2e()
- conn.delete_session(jid, sess.thread_id)
-
- self.roster.chg_contact_status(contact1, array[1], status_message,
- account)
- # Notifications
- if old_show < 2 and new_show > 1:
- notify.notify('contact_connected', jid, account, status_message)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('ContactPresence', (account,
- array))
-
- elif old_show > 1 and new_show < 2:
- notify.notify('contact_disconnected', jid, account, status_message)
- if self.remote_ctrl:
- 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])
- if self.remote_ctrl:
- 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))
-
- 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
- if (was_highest and not is_highest) or (not was_highest and is_highest):
- ctrl = self.msg_win_mgr.get_control(jid, account)
-
- if ctrl:
- ctrl.no_autonegotiation = False
- ctrl.set_session(None)
- ctrl.contact = highest
-
- def handle_event_msgerror(self, account, array):
- #'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]
-
- if array[1] == '503':
- # If we get server-not-found error, stop sending chatstates
- for contact in gajim.contacts.get_contacts(account, jid):
- contact.composing_xep = False
-
- session = None
- if len(array) > 5:
- session = array[5]
-
- gc_control = self.msg_win_mgr.get_gc_control(jid, account)
- if not gc_control and \
- jid in self.minimized_controls[account]:
- gc_control = self.minimized_controls[account][jid]
- if gc_control and gc_control.type_id != message_control.TYPE_GC:
- gc_control = None
- if gc_control:
- if len(jids) > 1: # it's a pm
- nick = jids[1]
-
- if session:
- ctrl = session.control
- else:
- ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account)
-
- if not ctrl:
- tv = gc_control.list_treeview
- model = tv.get_model()
- iter_ = gc_control.get_contact_iter(nick)
- if iter_:
- show = model[iter_][3]
- else:
- show = 'offline'
- gc_c = gajim.contacts.create_gc_contact(room_jid = jid,
- 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')
- 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:
- gc_control.set_subject(gc_control.subject)
- return
-
- if gajim.jid_is_transport(jid):
- jid = jid.replace('@', '')
- msg = array[2]
- if array[3]:
- msg = _('error while sending %(message)s ( %(error)s )') % {
- 'message': array[3], 'error': msg}
- if session:
- session.roster_message(jid, msg, array[4], msg_type='error')
-
- def handle_event_msgsent(self, account, array):
- #('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'):
- helpers.play_sound('message_sent')
-
- def handle_event_msgnotsent(self, account, array):
- #('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
- 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
- log.warn(msg)
- return
- array[4].roster_message(array[0], msg, array[3], account,
- msg_type='error')
-
- def handle_event_subscribe(self, account, array):
- #('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('Subscribe', (account, array))
-
- jid = array[0]
- text = array[1]
- nick = array[2]
- if helpers.allow_popup_window(account) or not self.systray_enabled:
- dialogs.SubscriptionRequestWindow(jid, text, account, nick)
- return
-
- self.add_event(account, jid, 'subscription_request', (text, nick))
-
- if helpers.allow_showing_notification(account):
- path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'subscription_request.png')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
- event_type = _('Subscription request')
- notify.popup(event_type, jid, account, 'subscription_request', path,
- event_type, jid)
-
- def handle_event_subscribed(self, account, array):
- #('SUBSCRIBED', account, (jid, resource))
- jid = array[0]
- if jid in gajim.contacts.get_jid_list(account):
- 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)
- else:
- keyID = ''
- attached_keys = gajim.config.get_per('accounts', account,
- '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, 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'):
- gajim.connections[account].ack_subscribed(jid)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('Subscribed', (account, array))
-
- def show_unsubscribed_dialog(self, account, contact):
- def on_yes(is_checked, list_):
- self.roster.on_req_usub(None, list_)
- list_ = [(contact, account)]
- dialogs.YesNoDialog(
- _('Contact "%s" removed subscription from you') % contact.jid,
- _('You will always see him or her as offline.\nDo you want to '
- 'remove him or her from your contact list?'),
- on_response_yes=(on_yes, list_))
- # FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does
- # not show deny
-
- def handle_event_unsubscribed(self, account, jid):
- #('UNSUBSCRIBED', account, jid)
- gajim.connections[account].ack_unsubscribed(jid)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('Unsubscribed', (account, jid))
-
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- if not contact:
- return
-
- if helpers.allow_popup_window(account) or not self.systray_enabled:
- self.show_unsubscribed_dialog(account, contact)
-
- self.add_event(account, jid, 'unsubscribed', contact)
-
- if helpers.allow_showing_notification(account):
- path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'unsubscribed.png')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
- event_type = _('Unsubscribed')
- notify.popup(event_type, jid, account, 'unsubscribed', path,
- event_type, jid)
-
- def handle_event_agent_info_error(self, account, agent):
- #('AGENT_ERROR_INFO', account, (agent))
- try:
- gajim.connections[account].services_cache.agent_info_error(agent)
- except AttributeError:
- return
-
- def handle_event_agent_items_error(self, account, agent):
- #('AGENT_ERROR_INFO', account, (agent))
- try:
- gajim.connections[account].services_cache.agent_items_error(agent)
- except AttributeError:
- return
-
- def handle_event_agent_removed(self, account, agent):
- # remove transport's contacts from treeview
- jid_list = gajim.contacts.get_jid_list(account)
- for jid in jid_list:
- if jid.endswith('@' + agent):
- c = gajim.contacts.get_first_contact_from_jid(account, jid)
- gajim.log.debug(
- 'Removing contact %s due to unregistered transport %s'\
- % (jid, agent))
- gajim.connections[account].unsubscribe(c.jid)
- # Transport contacts can't have 2 resources
- if c.jid in gajim.to_be_removed[account]:
- # This way we'll really remove it
- gajim.to_be_removed[account].remove(c.jid)
- self.roster.remove_contact(c.jid, account, backend=True)
-
- def handle_event_register_agent_info(self, account, array):
- # ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
- # info in a dataform if is_form is True
- if array[2] or 'instructions' in array[1]:
- config.ServiceRegistrationWindow(array[0], array[1], account,
- array[2])
- else:
- dialogs.ErrorDialog(_('Contact with "%s" cannot be established') \
- % array[0], _('Check your connection or try again later.'))
-
- def handle_event_agent_info_items(self, account, array):
- #('AGENT_INFO_ITEMS', account, (agent, node, items))
- our_jid = gajim.get_jid_from_account(account)
- if 'pep_services' in gajim.interface.instances[account] and \
- array[0] == our_jid:
- gajim.interface.instances[account]['pep_services'].items_received(
- array[2])
- try:
- gajim.connections[account].services_cache.agent_items(array[0],
- array[1], array[2])
- except AttributeError:
- return
-
- def handle_event_agent_info_info(self, account, array):
- #('AGENT_INFO_INFO', account, (agent, node, identities, features, data))
- try:
- gajim.connections[account].services_cache.agent_info(array[0],
- array[1], array[2], array[3], array[4])
- except AttributeError:
- return
-
- def handle_event_new_acc_connected(self, account, array):
- #('NEW_ACC_CONNECTED', account, (infos, is_form, ssl_msg, ssl_err,
- # ssl_cert, ssl_fingerprint))
- if 'account_creation_wizard' in self.instances:
- self.instances['account_creation_wizard'].new_acc_connected(array[0],
- array[1], array[2], array[3], array[4], array[5])
-
- def handle_event_new_acc_not_connected(self, account, array):
- #('NEW_ACC_NOT_CONNECTED', account, (reason))
- if 'account_creation_wizard' in self.instances:
- self.instances['account_creation_wizard'].new_acc_not_connected(array)
-
- def handle_event_acc_ok(self, account, array):
- #('ACC_OK', account, (config))
- if 'account_creation_wizard' in self.instances:
- self.instances['account_creation_wizard'].acc_is_ok(array)
-
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('NewAccount', (account, array))
-
- def handle_event_acc_not_ok(self, account, array):
- #('ACC_NOT_OK', account, (reason))
- if 'account_creation_wizard' in self.instances:
- self.instances['account_creation_wizard'].acc_is_not_ok(array)
-
- def handle_event_quit(self, p1, p2):
- self.roster.quit_gtkgui_interface()
-
- def handle_event_myvcard(self, account, array):
- nick = ''
- if 'NICKNAME' in array and array['NICKNAME']:
- gajim.nicks[account] = array['NICKNAME']
- elif 'FN' in array and array['FN']:
- gajim.nicks[account] = array['FN']
- if 'profile' in self.instances[account]:
- win = self.instances[account]['profile']
- win.set_values(array)
- if account in self.show_vcard_when_connect:
- self.show_vcard_when_connect.remove(account)
- jid = array['jid']
- if jid in self.instances[account]['infos']:
- self.instances[account]['infos'][jid].set_values(array)
-
- def handle_event_vcard(self, account, vcard):
- # ('VCARD', account, data)
- '''vcard holds the vcard data'''
- jid = vcard['jid']
- resource = vcard.get('resource', '')
- fjid = jid + '/' + str(resource)
-
- # vcard window
- win = None
- if jid in self.instances[account]['infos']:
- win = self.instances[account]['infos'][jid]
- elif resource and fjid in self.instances[account]['infos']:
- win = self.instances[account]['infos'][fjid]
- if win:
- win.set_values(vcard)
-
- # show avatar in chat
- ctrl = None
- if resource and self.msg_win_mgr.has_window(fjid, account):
- win = self.msg_win_mgr.get_window(fjid, account)
- ctrl = win.get_control(fjid, account)
- elif self.msg_win_mgr.has_window(jid, account):
- win = self.msg_win_mgr.get_window(jid, account)
- ctrl = win.get_control(jid, account)
-
- if ctrl and ctrl.type_id != message_control.TYPE_GC:
- ctrl.show_avatar()
-
- # Show avatar in roster or gc_roster
- gc_ctrl = self.msg_win_mgr.get_gc_control(jid, account)
- if not gc_ctrl and \
- jid in self.minimized_controls[account]:
- gc_ctrl = self.minimized_controls[account][jid]
- if gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC:
- gc_ctrl.draw_avatar(resource)
- else:
- self.roster.draw_avatar(jid, account)
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('VcardInfo', (account, vcard))
-
- def handle_event_last_status_time(self, account, array):
- # ('LAST_STATUS_TIME', account, (jid, resource, seconds, status))
- tim = array[2]
- if tim < 0:
- # Ann error occured
- return
- win = None
- if array[0] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0]]
- elif array[0] + '/' + array[1] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0] + '/' + array[1]]
- c = gajim.contacts.get_contact(account, array[0], array[1])
- if c: # c can be none if it's a gc contact
- c.last_status_time = time.localtime(time.time() - tim)
- if array[3]:
- c.status = array[3]
- self.roster.draw_contact(c.jid, account) # draw offline status
- if win:
- win.set_last_status_time()
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('LastStatusTime', (account, array))
-
- def handle_event_os_info(self, account, array):
- #'OS_INFO' (account, (jid, resource, client_info, os_info))
- win = None
- if array[0] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0]]
- elif array[0] + '/' + array[1] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0] + '/' + array[1]]
- if win:
- win.set_os_info(array[1], array[2], array[3])
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('OsInfo', (account, array))
-
- def handle_event_entity_time(self, account, array):
- #'ENTITY_TIME' (account, (jid, resource, time_info))
- win = None
- if array[0] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0]]
- elif array[0] + '/' + array[1] in self.instances[account]['infos']:
- win = self.instances[account]['infos'][array[0] + '/' + array[1]]
- if win:
- win.set_entity_time(array[1], array[2])
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('EntityTime', (account, array))
-
- 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))
- nick = array[3]
- if not nick:
- return
- room_jid = array[0]
- fjid = room_jid + '/' + nick
- show = array[1]
- status = array[2]
- conn = gajim.connections[account]
-
- # Get the window and control for the updated status, this may be a
- # PrivateChatControl
- control = self.msg_win_mgr.get_gc_control(room_jid, account)
-
- if not control and \
- 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):
- return
-
- control.chg_contact_status(nick, show, status, array[4], array[5],
- array[6], array[7], array[8], array[9], array[10], array[11])
-
- contact = gajim.contacts.\
- get_contact_with_highest_priority(account, room_jid)
- if contact:
- self.roster.draw_contact(room_jid, account)
-
- # print status in chat window and update status/GPG image
- ctrl = self.msg_win_mgr.get_control(fjid, account)
- if ctrl:
- 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)
- c = gajim.contacts.contact_from_gc_contact(gc_c)
- ctrl.gc_contact = gc_c
- ctrl.contact = c
- if ctrl.session:
- # stop e2e
- if ctrl.session.enable_encryption:
- thread_id = ctrl.session.thread_id
- ctrl.session.terminate_e2e()
- conn.delete_session(fjid, thread_id)
- ctrl.no_autonegotiation = False
- ctrl.draw_banner()
- old_jid = room_jid + '/' + nick
- new_jid = room_jid + '/' + new_nick
- self.msg_win_mgr.change_key(old_jid, new_jid, account)
- else:
- contact = ctrl.contact
- contact.show = show
- contact.status = status
- gc_contact = ctrl.gc_contact
- gc_contact.show = show
- gc_contact.status = status
- uf_show = helpers.get_uf_show(show)
- ctrl.print_conversation(_('%(nick)s is now %(status)s') % {
- '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(')', 'status', simple=True)
- ctrl.parent_win.redraw_tab(ctrl)
- ctrl.update_ui()
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('GCPresence', (account, array))
-
- def handle_event_gc_msg(self, account, array):
- # ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg,
- # [status_codes]))
- jids = array[0].split('/', 1)
- room_jid = jids[0]
-
- msg = array[1]
-
- gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
- if not gc_control and \
- room_jid in self.minimized_controls[account]:
- gc_control = self.minimized_controls[account][room_jid]
-
- if not gc_control:
- return
- xhtml = array[4]
-
- if gajim.config.get('ignore_incoming_xhtml'):
- xhtml = None
- if len(jids) == 1:
- # message from server
- nick = ''
- else:
- # message from someone
- nick = jids[1]
-
- gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5])
-
- if self.remote_ctrl:
- highlight = gc_control.needs_visual_notification(msg)
- array += (highlight,)
- self.remote_ctrl.raise_signal('GCMessage', (account, array))
-
- def handle_event_gc_subject(self, account, array):
- #('GC_SUBJECT', account, (jid, subject, body, has_timestamp))
- jids = array[0].split('/', 1)
- jid = jids[0]
-
- gc_control = self.msg_win_mgr.get_gc_control(jid, account)
-
- if not gc_control and \
- jid in self.minimized_controls[account]:
- gc_control = self.minimized_controls[account][jid]
-
- contact = gajim.contacts.\
- get_contact_with_highest_priority(account, jid)
- if contact:
- contact.status = array[1]
- self.roster.draw_contact(jid, account)
-
- if not gc_control:
- return
- gc_control.set_subject(array[1])
- # Standard way, the message comes from the occupant who set the subject
- text = None
- if len(jids) > 1:
- text = _('%(jid)s has set the subject to %(subject)s') % {
- 'jid': jids[1], 'subject': array[1]}
- # Workaround for psi bug http://flyspray.psi-im.org/task/595 , to be
- # deleted one day. We can receive a subject with a body that contains
- # "X has set the subject to Y" ...
- elif array[2]:
- text = array[2]
- if text is not None:
- if array[3]:
- gc_control.print_old_conversation(text)
- else:
- gc_control.print_conversation(text)
-
- def handle_event_gc_config(self, account, array):
- #('GC_CONFIG', account, (jid, form)) config is a dict
- room_jid = array[0].split('/')[0]
- if room_jid in gajim.automatic_rooms[account]:
- if 'continue_tag' in gajim.automatic_rooms[account][room_jid]:
- # We're converting chat to muc. allow participants to invite
- form = dataforms.ExtendForm(node = array[1])
- for f in form.iter_fields():
- if f.var == 'muc#roomconfig_allowinvites':
- f.value = True
- elif f.var == 'muc#roomconfig_publicroom':
- f.value = False
- elif f.var == 'muc#roomconfig_membersonly':
- f.value = True
- elif f.var == 'public_list':
- f.value = False
- gajim.connections[account].send_gc_config(room_jid, form)
- else:
- # use default configuration
- gajim.connections[account].send_gc_config(room_jid, array[1])
- # invite contacts
- # check if it is necessary to add
- continue_tag = False
- if 'continue_tag' in gajim.automatic_rooms[account][room_jid]:
- continue_tag = True
- if 'invities' in gajim.automatic_rooms[account][room_jid]:
- for jid in gajim.automatic_rooms[account][room_jid]['invities']:
- gajim.connections[account].send_invite(room_jid, jid,
- continue_tag=continue_tag)
- del gajim.automatic_rooms[account][room_jid]
- elif room_jid not in self.instances[account]['gc_config']:
- self.instances[account]['gc_config'][room_jid] = \
- config.GroupchatConfigWindow(account, room_jid, array[1])
-
- 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
- jid = array[0]
- statusCode = array[1]
-
- gc_control = self.msg_win_mgr.get_gc_control(jid, account)
- if not gc_control and \
- jid in self.minimized_controls[account]:
- gc_control = self.minimized_controls[account][jid]
- if not gc_control:
- return
-
- changes = []
- if '100' in statusCode:
- # Can be a presence (see chg_contact_status in groupchat_control.py)
- changes.append(_('Any occupant is allowed to see your full JID'))
- gc_control.is_anonymous = False
- if '102' in statusCode:
- changes.append(_('Room now shows unavailable member'))
- 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'))
- if '170' in statusCode:
- # Can be a presence (see chg_contact_status in groupchat_control.py)
- changes.append(_('Room logging is now enabled'))
- if '171' in statusCode:
- changes.append(_('Room logging is now disabled'))
- if '172' in statusCode:
- changes.append(_('Room is now non-anonymous'))
- gc_control.is_anonymous = False
- if '173' in statusCode:
- changes.append(_('Room is now semi-anonymous'))
- gc_control.is_anonymous = True
- if '174' in statusCode:
- changes.append(_('Room is now fully-anonymous'))
- gc_control.is_anonymous = True
-
- for change in changes:
- gc_control.print_conversation(change)
-
- def handle_event_gc_affiliation(self, account, array):
- #('GC_AFFILIATION', account, (room_jid, users_dict))
- room_jid = array[0]
- if room_jid in self.instances[account]['gc_config']:
- self.instances[account]['gc_config'][room_jid].\
- affiliation_list_received(array[1])
-
- def handle_event_gc_password_required(self, account, array):
- #('GC_PASSWORD_REQUIRED', account, (room_jid, nick))
- room_jid = array[0]
- nick = array[1]
-
- def on_ok(text):
- gajim.connections[account].join_gc(nick, room_jid, text)
- gajim.gc_passwords[room_jid] = text
-
- def on_cancel():
- # get and destroy window
- if room_jid in gajim.interface.minimized_controls[account]:
- self.roster.on_disconnect(None, room_jid, account)
- else:
- win = self.msg_win_mgr.get_window(room_jid, account)
- ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
- 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)
- dlg.input_entry.set_visibility(False)
-
- def handle_event_gc_invitation(self, account, array):
- #('GC_INVITATION', (room_jid, jid_from, reason, password, is_continued))
- jid = gajim.get_jid_without_resource(array[1])
- room_jid = array[0]
- if helpers.allow_popup_window(account) or not self.systray_enabled:
- dialogs.InvitationReceivedDialog(account, room_jid, jid, array[3],
- array[2], is_continued=array[4])
- return
-
- self.add_event(account, jid, 'gc-invitation', (room_jid, array[2],
- array[3], array[4]))
-
- if helpers.allow_showing_notification(account):
- path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'gc_invitation.png')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(path)
- event_type = _('Groupchat Invitation')
- notify.popup(event_type, jid, account, 'gc-invitation', path,
- event_type, room_jid)
-
- def forget_gpg_passphrase(self, keyid):
- if keyid in self.gpg_passphrase:
- del self.gpg_passphrase[keyid]
- return False
-
- def handle_event_bad_passphrase(self, account, array):
- #('BAD_PASSPHRASE', account, ())
- 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.')
- dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
- else:
- path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'warning.png')
- notify.popup('warning', account, account, 'warning', path,
- _('OpenGPG Passphrase Incorrect'),
- _('You are currently connected without your OpenPGP key.'))
- keyID = gajim.config.get_per('accounts', account, 'keyid')
- self.forget_gpg_passphrase(keyID)
-
- def handle_event_gpg_password_required(self, account, array):
- #('GPG_PASSWORD_REQUIRED', account, (callback,))
- callback = array[0]
- keyid = gajim.config.get_per('accounts', account, 'keyid')
- if keyid in self.gpg_passphrase:
- request = self.gpg_passphrase[keyid]
- else:
- request = PassphraseRequest(keyid)
- self.gpg_passphrase[keyid] = request
- request.add_callback(account, callback)
-
- def handle_event_gpg_always_trust(self, account, callback):
- #('GPG_ALWAYS_TRUST', account, callback)
- def on_yes(checked):
- if checked:
- gajim.connections[account].gpg.always_trust = True
- callback(True)
-
- def on_no():
- 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)
-
- def handle_event_password_required(self, account, array):
- #('PASSWORD_REQUIRED', account, None)
- if account in self.pass_dialog:
- return
- text = _('Enter your password for account %s') % account
- 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)')
-
- def on_ok(passphrase, save):
- if save:
- gajim.config.set_per('accounts', account, 'savepass', True)
- passwords.save_password(account, passphrase)
- gajim.connections[account].set_password(passphrase)
- del self.pass_dialog[account]
-
- def on_cancel():
- self.roster.set_state(account, 'offline')
- self.roster.update_status_combobox()
- del self.pass_dialog[account]
-
- self.pass_dialog[account] = dialogs.PassphraseDialog(
- _('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))
- jid = array[0]
- name = array[1]
- sub = array[2]
- ask = array[3]
- groups = array[4]
- contacts = gajim.contacts.get_contacts(account, jid)
- if (not sub or sub == 'none') and (not ask or ask == 'none') and \
- not name and not groups:
- # contact removed us.
- if contacts:
- self.roster.remove_contact(jid, account, backend=True)
- return
- elif not contacts:
- if sub == 'remove':
- return
- # Add new contact to roster
- contact = gajim.contacts.create_contact(jid=jid, name=name,
- groups=groups, show='offline', sub=sub, ask=ask)
- 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
- 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
- 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)
-
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('RosterInfo', (account, array))
-
- def handle_event_bookmarks(self, account, bms):
- # ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
- # We received a bookmark item from the server (JEP48)
- # Auto join GC windows if neccessary
-
- self.roster.set_actions_menu_needs_rebuild()
- invisible_show = gajim.SHOW_LIST.index('invisible')
- # do not autojoin if we are invisible
- if gajim.connections[account].connected == invisible_show:
- return
-
- self.auto_join_bookmarks(account)
-
- def handle_event_file_send_error(self, account, array):
- jid = array[0]
- file_props = array[1]
- ft = self.instances['file_transfers']
- ft.set_status(file_props['type'], file_props['sid'], 'stop')
-
- if helpers.allow_popup_window(account):
- ft.show_send_error(file_props)
- return
-
- self.add_event(account, jid, 'file-send-error', file_props)
-
- if helpers.allow_showing_notification(account):
- img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
- event_type = _('File Transfer Error')
- notify.popup(event_type, jid, account, 'file-send-error', path,
- event_type, file_props['name'])
-
- def handle_event_gmail_notify(self, account, array):
- jid = array[0]
- gmail_new_messages = int(array[1])
- gmail_messages_list = array[2]
- if gajim.config.get('notify_on_new_gmail_email'):
- img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'new_email_recv.png')
- title = _('New mail on %(gmail_mail_address)s') % \
- {'gmail_mail_address': jid}
- text = i18n.ngettext('You have %d new mail conversation',
- 'You have %d new mail conversations', gmail_new_messages,
- gmail_new_messages, gmail_new_messages)
-
- 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:
- 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']}
- cnt += 1
-
- if gajim.config.get_per('soundevents', 'gmail_received', 'enabled'):
- helpers.play_sound('gmail_received')
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
- notify.popup(_('New E-mail'), jid, account, 'gmail',
- path_to_image=path, title=title,
- text=text)
-
- if self.remote_ctrl:
- self.remote_ctrl.raise_signal('NewGmail', (account, array))
-
- def handle_event_file_request_error(self, account, array):
- # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
- jid, file_props, errmsg = array
- ft = self.instances['file_transfers']
- ft.set_status(file_props['type'], file_props['sid'], 'stop')
- errno = file_props['error']
-
- if helpers.allow_popup_window(account):
- if errno in (-4, -5):
- ft.show_stopped(jid, file_props, errmsg)
- else:
- ft.show_request_error(file_props)
- return
-
- if errno in (-4, -5):
- msg_type = 'file-error'
- else:
- msg_type = 'file-request-error'
-
- self.add_event(account, jid, msg_type, file_props)
-
- if helpers.allow_showing_notification(account):
- # check if we should be notified
- img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
-
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
- event_type = _('File Transfer Error')
- notify.popup(event_type, jid, account, msg_type, path,
- title = event_type, text = file_props['name'])
-
- def handle_event_file_request(self, account, array):
- jid = array[0]
- if jid not in gajim.contacts.get_jid_list(account):
- keyID = ''
- attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
- if jid in attached_keys:
- keyID = attached_keys[attached_keys.index(jid) + 1]
- contact = gajim.contacts.create_contact(jid=jid, name='',
- groups=[_('Not in Roster')], show='not in roster', status='',
- sub='none', keyID=keyID)
- gajim.contacts.add_contact(account, contact)
- self.roster.add_contact(contact.jid, account)
- file_props = array[1]
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
-
- if helpers.allow_popup_window(account):
- self.instances['file_transfers'].show_file_request(account, contact,
- file_props)
- return
-
- self.add_event(account, jid, 'file-request', file_props)
-
- if helpers.allow_showing_notification(account):
- img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
- 'ft_request.png')
- txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(
- account, jid)
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
- event_type = _('File Transfer Request')
- notify.popup(event_type, jid, account, 'file-request',
- path_to_image = path, title = event_type, text = txt)
-
- def handle_event_file_error(self, title, message):
- dialogs.ErrorDialog(title, message)
-
- def handle_event_file_progress(self, account, file_props):
- if time.time() - self.last_ftwindow_update > 0.5:
- # update ft window every 500ms
- self.last_ftwindow_update = time.time()
- self.instances['file_transfers'].set_progress(file_props['type'],
- file_props['sid'], file_props['received-len'])
-
- def handle_event_file_rcv_completed(self, account, file_props):
- ft = self.instances['file_transfers']
- if file_props['error'] == 0:
- ft.set_progress(file_props['type'], file_props['sid'],
- file_props['received-len'])
- else:
- ft.set_status(file_props['type'], file_props['sid'], 'stop')
- if 'stalled' in file_props and file_props['stalled'] or \
- 'paused' in file_props and file_props['paused']:
- return
- if file_props['type'] == 'r': # we receive a file
- jid = unicode(file_props['sender'])
- else: # we send a file
- jid = unicode(file_props['receiver'])
-
- if helpers.allow_popup_window(account):
- if file_props['error'] == 0:
- if gajim.config.get('notify_on_file_complete'):
- ft.show_completed(jid, file_props)
- elif file_props['error'] == -1:
- 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'))
- return
-
- msg_type = ''
- event_type = ''
- if file_props['error'] == 0 and gajim.config.get(
- 'notify_on_file_complete'):
- msg_type = 'file-completed'
- event_type = _('File Transfer Completed')
- elif file_props['error'] in (-1, -6):
- msg_type = 'file-stopped'
- event_type = _('File Transfer Stopped')
-
- 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
- return
-
- if msg_type:
- self.add_event(account, jid, msg_type, file_props)
-
- if file_props is not None:
- if file_props['type'] == 'r':
- # get the name of the sender, as it is in the roster
- sender = unicode(file_props['sender']).split('/')[0]
- name = gajim.contacts.get_first_contact_from_jid(account,
- 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}
- img = 'ft_done.png'
- else: # ft stopped
- txt = _('File transfer of %(filename)s from %(name)s stopped.')\
- % {'filename': filename, 'name': name}
- img = 'ft_stopped.png'
- else:
- receiver = file_props['receiver']
- if hasattr(receiver, 'jid'):
- receiver = receiver.jid
- receiver = receiver.split('/')[0]
- # get the name of the contact, as it is in the roster
- name = gajim.contacts.get_first_contact_from_jid(account,
- receiver).get_shown_name()
- filename = os.path.basename(file_props['file-name'])
- if event_type == _('File Transfer Completed'):
- txt = _('You successfully sent %(filename)s to %(name)s.')\
- % {'filename': filename, 'name': name}
- img = 'ft_done.png'
- else: # ft stopped
- txt = _('File transfer of %(filename)s to %(name)s stopped.')\
- % {'filename': filename, 'name': name}
- img = 'ft_stopped.png'
- img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', img)
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
- else:
- txt = ''
-
- if gajim.config.get('notify_on_file_complete') and \
- (gajim.config.get('autopopupaway') or \
- gajim.connections[account].connected in (2, 3)):
- # we want to be notified and we are online/chat or we don't mind
- # bugged when away/na/busy
- notify.popup(event_type, jid, account, msg_type, path_to_image = path,
- title = event_type, text = txt)
-
- def handle_event_stanza_arrived(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, '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')
-
- 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():
- 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)
-
- def handle_event_vcard_not_published(self, account, array):
- if 'profile' in self.instances[account]:
- win = self.instances[account]['profile']
- win.vcard_not_published()
-
- 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)
-
- def handle_event_signed_in(self, account, empty):
- '''SIGNED_IN event is emitted when we sign in, so handle it'''
- # block signed in notifications for 30 seconds
- gajim.block_signed_in_notifications[account] = True
- self.roster.set_actions_menu_needs_rebuild()
- self.roster.draw_account(account)
- state = self.sleeper.getState()
- connected = gajim.connections[account].connected
- if gajim.config.get('ask_offline_status_on_connection'):
- # Ask offline status in 1 minute so w'are sure we got all online
- # presences
- gobject.timeout_add_seconds(60, self.ask_offline_status, account)
- if state != common.sleepy.STATE_UNKNOWN and connected in (2, 3):
- # we go online or free for chat, so we activate auto status
- 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
- # Else disable autoaway
- gajim.sleeper_state[account] = 'off'
- invisible_show = gajim.SHOW_LIST.index('invisible')
- # We cannot join rooms if we are invisible
- 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():
- 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]:
- continue
- nick = gc_control.nick
- password = gajim.gc_passwords.get(room_jid, '')
- gajim.connections[account].join_gc(nick, room_jid, password)
-
- def handle_event_metacontacts(self, account, tags_list):
- gajim.contacts.define_metacontacts(account, tags_list)
- self.roster.redraw_metacontacts(account)
-
- def handle_atom_entry(self, account, data):
- atom_entry, = data
- AtomWindow.newAtomEntry(atom_entry)
-
- def handle_event_failed_decrypt(self, account, data):
- jid, tim, session = data
-
- details = _('Unable to decrypt message from '
- '%s\nIt may have been tampered with.') % jid
-
- ctrl = session.control
- if ctrl:
- ctrl.print_conversation_line(details, 'status', '', tim)
- else:
- dialogs.WarningDialog(_('Unable to decrypt message'),
- details)
-
- # terminate the session
- session.terminate_e2e()
- session.conn.delete_session(jid, session.thread_id)
-
- # restart the session
- if ctrl:
- ctrl.begin_e2e_negotiation()
-
- def handle_event_privacy_lists_received(self, account, data):
- # ('PRIVACY_LISTS_RECEIVED', account, list)
- if account not in self.instances:
- return
- if 'privacy_lists' in self.instances[account]:
- self.instances[account]['privacy_lists'].privacy_lists_received(data)
-
- def handle_event_privacy_list_received(self, account, data):
- # ('PRIVACY_LIST_RECEIVED', account, (name, rules))
- if account not in self.instances:
- return
- name = data[0]
- rules = data[1]
- if 'privacy_list_%s' % name in self.instances[account]:
- self.instances[account]['privacy_list_%s' % name].\
- privacy_list_received(rules)
- if name == 'block':
- gajim.connections[account].blocked_contacts = []
- gajim.connections[account].blocked_groups = []
- gajim.connections[account].blocked_list = []
- gajim.connections[account].blocked_all = False
- for rule in rules:
- 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'])
- elif rule['type'] == 'group' and rule['action'] == 'deny':
- 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)
-
- def handle_event_privacy_lists_active_default(self, account, data):
- if not data:
- return
- # Send to all privacy_list_* windows as we can't know which one asked
- for win in self.instances[account]:
- if win.startswith('privacy_list_'):
- self.instances[account][win].check_active_default(data)
-
- def handle_event_privacy_list_removed(self, account, name):
- # ('PRIVACY_LISTS_REMOVED', account, name)
- if account not in self.instances:
- return
- if 'privacy_lists' in self.instances[account]:
- self.instances[account]['privacy_lists'].privacy_list_removed(name)
-
- def handle_event_zc_name_conflict(self, account, data):
- def on_ok(new_name):
- gajim.config.set_per('accounts', account, 'name', new_name)
- status = gajim.connections[account].status
- gajim.connections[account].username = new_name
- gajim.connections[account].change_status(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)
-
- def handle_event_ping_sent(self, account, contact):
- if contact.jid == contact.get_full_jid():
- # If contact is a groupchat user
- jids = [contact.jid]
- else:
- jids = [contact.jid, contact.get_full_jid()]
- for jid in jids:
- ctrl = self.msg_win_mgr.get_control(jid, account)
- if ctrl:
- ctrl.print_conversation(_('Ping?'), 'status')
-
- def handle_event_ping_reply(self, account, data):
- contact = data[0]
- seconds = data[1]
- if contact.jid == contact.get_full_jid():
- # If contact is a groupchat user
- jids = [contact.jid]
- else:
- jids = [contact.jid, contact.get_full_jid()]
- for jid in jids:
- ctrl = self.msg_win_mgr.get_control(jid, account)
- if ctrl:
- ctrl.print_conversation(_('Pong! (%s s.)') % seconds, 'status')
-
- def handle_event_ping_error(self, account, contact):
- if contact.jid == contact.get_full_jid():
- # If contact is a groupchat user
- jids = [contact.jid]
- else:
- jids = [contact.jid, contact.get_full_jid()]
- for jid in jids:
- ctrl = self.msg_win_mgr.get_control(jid, account)
- if ctrl:
- ctrl.print_conversation(_('Error.'), 'status')
-
- def handle_event_search_form(self, account, data):
- # ('SEARCH_FORM', account, (jid, dataform, is_dataform))
- if data[0] not in self.instances[account]['search']:
- return
- self.instances[account]['search'][data[0]].on_form_arrived(data[1],
- data[2])
-
- def handle_event_search_result(self, account, data):
- # ('SEARCH_RESULT', account, (jid, dataform, is_dataform))
- if data[0] not in self.instances[account]['search']:
- return
- self.instances[account]['search'][data[0]].on_result_arrived(data[1],
- data[2])
-
- def handle_event_resource_conflict(self, account, data):
- # ('RESOURCE_CONFLICT', account, ())
- # First we go offline, but we don't overwrite status message
- self.roster.send_status(account, 'offline',
- 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)
- dlg = dialogs.InputDialog(_('Resource Conflict'),
- _('You are already connected to this account with the same resource. Please type a new one'), input_str = gajim.connections[account].server_resource,
- is_modal = False, ok_handler = on_ok)
-
- def handle_event_pep_config(self, account, data):
- # ('PEP_CONFIG', account, (node, form))
- if 'pep_services' in self.instances[account]:
- self.instances[account]['pep_services'].config(data[0], data[1])
-
- def handle_event_roster_item_exchange(self, account, data):
- # data = (action in [add, delete, modify], exchange_list, jid_from)
- dialogs.RosterItemExchangeWindow(account, data[0], data[1], data[2])
-
- def handle_event_unique_room_id_supported(self, account, data):
- '''Receive confirmation that unique_room_id are supported'''
- # ('UNIQUE_ROOM_ID_SUPPORTED', server, instance, room_id)
- instance = data[1]
- instance.unique_room_id_supported(data[0], data[2])
-
- def handle_event_unique_room_id_unsupported(self, account, data):
- # ('UNIQUE_ROOM_ID_UNSUPPORTED', server, instance)
- instance = data[1]
- instance.unique_room_id_error(data[0])
-
- def handle_event_ssl_error(self, account, data):
- # ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint))
- server = gajim.config.get_per('accounts', account, 'hostname')
-
- def on_ok(is_checked):
- del self.instances[account]['online_dialog']['ssl_error']
- if is_checked[0]:
- # Check if cert is already in file
- certs = ''
- if os.path.isfile(gajim.MY_CACERTS):
- f = open(gajim.MY_CACERTS)
- certs = f.read()
- 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)
- 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])
- if is_checked[1]:
- ignore_ssl_errors = gajim.config.get_per('accounts', account,
- 'ignore_ssl_errors').split()
- ignore_ssl_errors.append(str(data[1]))
- gajim.config.set_per('accounts', account, 'ignore_ssl_errors',
- ' '.join(ignore_ssl_errors))
- gajim.connections[account].ssl_certificate_accepted()
-
- def on_cancel():
- del self.instances[account]['online_dialog']['ssl_error']
- gajim.connections[account].disconnect(on_purpose=True)
- 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]}
- 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]
- 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)
-
- 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])
- # Reset the ignored ssl errors
- gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '')
- gajim.connections[account].ssl_certificate_accepted()
- def on_no():
- del self.instances[account]['online_dialog']['fingerprint_error']
- gajim.connections[account].disconnect(on_purpose=True)
- 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]}
- if 'fingerprint_error' in self.instances[account]['online_dialog']:
- 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)
-
- 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()
- return
- # On cancel call del self.instances, so don't call it another time
- # before
- del self.instances[account]['online_dialog']['plain_connection']
- if is_checked[1]:
- gajim.config.set_per('accounts', account,
- 'warn_when_plaintext_connection', False)
- gajim.connections[account].connection_accepted(data[0], 'plain')
- def on_cancel():
- del self.instances[account]['online_dialog']['plain_connection']
- 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?')
- checktext1 = _('Yes, I really want to connect insecurely')
- 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'] = \
- dialogs.ConfirmationDialogDubbleCheck(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):
- del self.instances[account]['online_dialog']['insecure_ssl']
- if not is_checked[0]:
- on_cancel()
- return
- if is_checked[1]:
- gajim.config.set_per('accounts', account,
- 'warn_when_insecure_ssl_connection', 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].connection_accepted(data[0], data[1])
- def on_cancel():
- del self.instances[account]['online_dialog']['insecure_ssl']
- 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 insecure '
- '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')
- 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)
-
- 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 register_handler(self, event, handler):
- if event not in self.handlers:
- self.handlers[event] = []
-
- if handler not in self.handlers[event]:
- self.handlers[event].append(handler)
-
- def unregister_handler(self, event, handler):
- self.handlers[event].remove(handler)
-
- def register_handlers(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],
- '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_ERROR_INFO': [self.handle_event_agent_info_error],
- 'AGENT_ERROR_ITEMS': [self.handle_event_agent_items_error],
- '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],
- 'AGENT_INFO_INFO': [self.handle_event_agent_info_info],
- 'QUIT': [self.handle_event_quit],
- 'NEW_ACC_CONNECTED': [self.handle_event_new_acc_connected],
- 'NEW_ACC_NOT_CONNECTED': [self.handle_event_new_acc_not_connected],
- 'ACC_OK': [self.handle_event_acc_ok],
- 'ACC_NOT_OK': [self.handle_event_acc_not_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],
- }
-
- def dispatch(self, event, account, data):
- '''
- Dispatches an network event to the event handlers of this class.
-
- Return true if it could be dispatched to alteast one handler.
- '''
- if event not in self.handlers:
- log.warning('Unknown event %s dispatched to GUI: %s' % (event, data))
- return False
- else:
- log.debug('Event %s distpached to GUI: %s' % (event, data))
- for handler in self.handlers[event]:
- handler(account, data)
- return len(self.handlers[event])
-
-
-################################################################################
-### Methods dealing with gajim.events
-################################################################################
-
- def add_event(self, account, jid, type_, event_args):
- '''add an event to the gajim.events var'''
- # We add it to the gajim.events queue
- # 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
- # event_type can be in advancedNotificationWindow.events_list
- event_types = {'file-request': 'ft_request',
- '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)
- 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 type_ == 'gc-invitation':
- self.roster.add_groupchat(jid, account, status='offline')
- else:
- # add contact to roster ("Not In The Roster") if he is not
- self.roster.add_to_not_in_the_roster(account, jid)
- else:
- self.roster.draw_contact(jid, account)
-
- # Select the big brother contact in roster, it's visible because it has
- # events.
- family = gajim.contacts.get_metacontacts_family(account, jid)
- if family:
- nearby_family, bb_jid, bb_account = \
- self.roster._get_nearby_family_and_big_brother(family, account)
- else:
- bb_jid, bb_account = jid, account
- self.roster.select_contact(bb_jid, bb_account)
-
- def handle_event(self, account, fjid, type_):
- w = None
- ctrl = None
- session = None
-
- resource = gajim.get_resource_from_jid(fjid)
- jid = gajim.get_jid_without_resource(fjid)
-
- if type_ in ('printed_gc_msg', 'printed_marked_gc_msg', 'gc_msg'):
- w = self.msg_win_mgr.get_window(jid, account)
- if jid in self.minimized_controls[account]:
- self.roster.on_groupchat_maximized(None, jid, account)
- return
- else:
- ctrl = self.msg_win_mgr.get_gc_control(jid, account)
-
- elif type_ in ('printed_chat', 'chat', ''):
- # '' is for log in/out notifications
-
- if type_ != '':
- event = gajim.events.get_first_event(account, fjid, type_)
- if not event:
- event = gajim.events.get_first_event(account, jid, type_)
- if not event:
- return
-
- if type_ == 'printed_chat':
- ctrl = event.parameters[0]
- elif type_ == 'chat':
- session = event.parameters[8]
- ctrl = session.control
- elif type_ == '':
- ctrl = self.msg_win_mgr.get_control(fjid, account)
-
- if not ctrl:
- 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 \
- not self.msg_win_mgr.has_window(jid, account):
- # remove resource of events too
- gajim.events.change_jid(account, fjid, jid)
- resource = None
- fjid = jid
- contact = None
- if resource:
- contact = gajim.contacts.get_contact(account, jid, resource)
- if not contact:
- contact = highest_contact
-
- 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
- event = gajim.events.get_first_event(account, fjid, type_)
- if not event:
- event = gajim.events.get_first_event(account, jid, type_)
-
- if type_ == 'printed_pm':
- ctrl = event.parameters[0]
- elif type_ == 'pm':
- session = event.parameters[8]
-
- if session and session.control:
- ctrl = session.control
- elif not ctrl:
- room_jid = jid
- nick = resource
- gc_contact = gajim.contacts.get_gc_contact(account, room_jid,
- nick)
- if gc_contact:
- show = gc_contact.show
- else:
- show = 'offline'
- gc_contact = gajim.contacts.create_gc_contact(
- room_jid = room_jid, name = nick, show = show)
-
- if not session:
- session = gajim.connections[account].make_new_session(
- fjid, None, type_='pm')
-
- self.new_private_chat(gc_contact, account, session=session)
- ctrl = session.control
-
- w = ctrl.parent_win
- elif type_ in ('normal', 'file-request', 'file-request-error',
- 'file-send-error', 'file-error', 'file-stopped', 'file-completed'):
- # Get the first single message event
- event = gajim.events.get_first_event(account, fjid, type_)
- if not event:
- # default to jid without resource
- event = gajim.events.get_first_event(account, jid, type_)
- if not event:
- return
- # Open the window
- self.roster.open_event(account, jid, event)
- else:
- # Open the window
- self.roster.open_event(account, fjid, event)
- elif type_ == 'gmail':
- url=gajim.connections[account].gmail_url
- if url:
- helpers.launch_browser_mailer('url', url)
- elif type_ == 'gc-invitation':
- event = gajim.events.get_first_event(account, jid, type_)
- data = event.parameters
- dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
- data[1], data[3])
- gajim.events.remove_events(account, jid, event)
- self.roster.draw_contact(jid, account)
- elif type_ == 'subscription_request':
- event = gajim.events.get_first_event(account, jid, type_)
- data = event.parameters
- dialogs.SubscriptionRequestWindow(jid, data[0], account, data[1])
- gajim.events.remove_events(account, jid, event)
- self.roster.draw_contact(jid, account)
- elif type_ == 'unsubscribed':
- event = gajim.events.get_first_event(account, jid, type_)
- contact = event.parameters
- self.show_unsubscribed_dialog(account, contact)
- gajim.events.remove_events(account, jid, event)
- self.roster.draw_contact(jid, account)
- if w:
- w.set_active_tab(ctrl)
- w.window.window.focus(gtk.get_current_event_time())
- # Using isinstance here because we want to catch all derived types
- if isinstance(ctrl, ChatControlBase):
- tv = ctrl.conv_textview
- tv.scroll_to_end()
-
-################################################################################
-### Methods dealing with emoticons
-################################################################################
-
- def image_is_ok(self, image):
- if not os.path.exists(image):
- return False
- img = gtk.Image()
- try:
- img.set_from_file(image)
- except Exception:
- return False
- t = img.get_storage_type()
- if t != gtk.IMAGE_PIXBUF and t != gtk.IMAGE_ANIMATION:
- return False
- return True
-
- @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
-
- @property
- def emot_and_basic_re(self):
- try:
- return self._emot_and_basic_re
- except AttributeError:
- self._emot_and_basic_re = re.compile(self.emot_and_basic,
- re.IGNORECASE + re.UNICODE)
- 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:
- self._sth_at_sth_dot_sth_re = re.compile(self.sth_at_sth_dot_sth)
- return self._sth_at_sth_dot_sth_re
-
- @property
- def invalid_XML_chars_re(self):
- try:
- return self._invalid_XML_chars_re
- except AttributeError:
- self._invalid_XML_chars_re = re.compile(self.invalid_XML_chars)
- return self._invalid_XML_chars_re
-
- def make_regexps(self):
- # regexp meta characters are: . ^ $ * + ? { } [ ] \ | ( )
- # one escapes the metachars with \
- # \S matches anything but ' ' '\t' '\n' '\r' '\f' and '\v'
- # \s matches any whitespace character
- # \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 at the beginning of lines
- #
- # * means 0 or more times
- # + means 1 or more times
- # ? means 0 or 1 time
- # | means or
- # [^*] anything but '*' (inside [] you don't have to escape metachars)
- # [^\s*] anything but whitespaces and '*'
- # (? 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})+)"
- # NOTE: it's ok to catch www.gr such stuff exist!
-
- #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})+)"
-
- #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*
- formatting = r'|(?> 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)
- sys.exit()
-
- def save_avatar_files(self, jid, photo, puny_nick = None, local = False):
- '''Saves an avatar to a separate file, and generate files for dbus notifications. An avatar can be given as a pixmap directly or as an decoded image.'''
- puny_jid = helpers.sanitize_filename(jid)
- path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid)
- if puny_nick:
- path_to_file = os.path.join(path_to_file, puny_nick)
- # remove old avatars
- for typ in ('jpeg', 'png'):
- if local:
- path_to_original_file = path_to_file + '_local'+ '.' + typ
- else:
- path_to_original_file = path_to_file + '.' + typ
- if os.path.isfile(path_to_original_file):
- os.remove(path_to_original_file)
- if local and photo:
- pixbuf = photo
- typ = 'png'
- extension = '_local.png' # save local avatars as png file
- else:
- 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))
- 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)))
- # 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
- 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)))
- # Generate and save the resized, black and white avatar
- bwbuf = gtkgui_helpers.get_scaled_pixbuf(
- 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)))
-
- def remove_avatar_files(self, jid, puny_nick = None, local = False):
- '''remove avatar files of a jid'''
- puny_jid = helpers.sanitize_filename(jid)
- path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid)
- if puny_nick:
- path_to_file = os.path.join(path_to_file, puny_nick)
- for ext in ('.jpeg', '.png'):
- if local:
- ext = '_local' + ext
- path_to_original_file = path_to_file + ext
- if os.path.isfile(path_to_file + ext):
- os.remove(path_to_file + ext)
- if os.path.isfile(path_to_file + '_notif_size_colored' + ext):
- os.remove(path_to_file + '_notif_size_colored' + ext)
- if os.path.isfile(path_to_file + '_notif_size_bw' + ext):
- os.remove(path_to_file + '_notif_size_bw' + ext)
-
- def auto_join_bookmarks(self, account):
- '''autojoin bookmarked GCs that have 'auto join' on for this account'''
- for bm in gajim.connections[account].bookmarks:
- if bm['autojoin'] in ('1', 'true'):
- jid = bm['jid']
- # Only join non-opened groupchats. Opened one are already
- # auto-joined on re-connection
- if not jid in gajim.gc_connected[account]:
- # we are not already connected
- minimize = bm['minimize'] in ('1', 'true')
- gajim.interface.join_gc_room(account, jid, bm['nick'],
- bm['password'], minimize = minimize)
- 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.
- self.roster.add_groupchat(jid, account)
-
- def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
- nick):
- '''add a bookmark for this account, sorted in bookmark list'''
- bm = {
- 'name': name,
- 'jid': jid,
- 'autojoin': autojoin,
- 'minimize': minimize,
- 'password': password,
- 'nick': nick
- }
- place_found = False
- index = 0
- # check for duplicate entry and respect alpha order
- 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'])
- return
- if bookmark['name'] > bm['name']:
- place_found = True
- break
- index += 1
- if place_found:
- gajim.connections[account].bookmarks.insert(index, bm)
- else:
- gajim.connections[account].bookmarks.append(bm)
- 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.'))
-
-
- # does JID exist only within a groupchat?
- def is_pm_contact(self, fjid, account):
- bare_jid = gajim.get_jid_without_resource(fjid)
-
- gc_ctrl = self.msg_win_mgr.get_gc_control(bare_jid, account)
-
- if not gc_ctrl and \
- bare_jid in self.minimized_controls[account]:
- gc_ctrl = self.minimized_controls[account][bare_jid]
-
- return gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC
-
- def create_ipython_window(self):
- try:
- from ipython_view import IPythonView
- except ImportError:
- print 'ipython_view not found'
- return
- import pango
-
- if os.name == 'nt':
- font = 'Lucida Console 9'
- else:
- font = 'Luxi Mono 10'
-
- window = gtk.Window()
- window.set_size_request(750,550)
- window.set_resizable(True)
- sw = gtk.ScrolledWindow()
- sw.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
- view = IPythonView()
- view.modify_font(pango.FontDescription(font))
- view.set_wrap_mode(gtk.WRAP_CHAR)
- sw.add(view)
- window.add(sw)
- window.show_all()
- def on_delete(win, event):
- win.hide()
- return True
- window.connect('delete_event',on_delete)
- view.updateNamespace({'gajim': gajim})
- gajim.ipython_window = window
-
- def run(self):
- if self.systray_capabilities and gajim.config.get('trayicon') != 'never':
- self.show_systray()
-
- self.roster = roster_window.RosterWindow()
- for account in gajim.connections:
- gajim.connections[account].load_roster_from_db()
-
- # get instances for windows/dialogs that will show_all()/hide()
- self.instances['file_transfers'] = dialogs.FileTransfersWindow()
-
- gobject.timeout_add(100, self.autoconnect)
- timeout, in_seconds = gajim.idlequeue.PROCESS_TIMEOUT
- if in_seconds:
- gobject.timeout_add_seconds(timeout, self.process_connections)
- else:
- gobject.timeout_add(timeout, self.process_connections)
- gobject.timeout_add_seconds(gajim.config.get(
- 'check_idle_every_foo_seconds'), self.read_sleepy)
-
- # when using libasyncns we need to process resolver in regular intervals
- 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:
- import remote_control
- self.remote_ctrl = remote_control.Remote()
- except Exception:
- pass
- gobject.timeout_add_seconds(5, remote_init)
-
- for account in gajim.connections:
- if gajim.config.get_per('accounts', account, 'publish_tune') and \
- dbus_support.supported:
- self.enable_music_listener()
- break
-
- def __init__(self):
- gajim.interface = self
- gajim.thread_interface = ThreadInterface
- # This is the manager and factory of message windows set by the module
- self.msg_win_mgr = None
- self.jabber_state_images = {'16': {}, '32': {}, 'opened': {},
- 'closed': {}}
- self.emoticons_menu = None
- # handler when an emoticon is clicked in emoticons_menu
- self.emoticon_menuitem_clicked = None
- self.minimized_controls = {}
- self.status_sent_to_users = {}
- self.status_sent_to_groups = {}
- self.gpg_passphrase = {}
- self.pass_dialog = {}
- self.default_colors = {
- 'inmsgcolor': gajim.config.get('inmsgcolor'),
- 'outmsgcolor': gajim.config.get('outmsgcolor'),
- 'statusmsgcolor': gajim.config.get('statusmsgcolor'),
- 'urlmsgcolor': gajim.config.get('urlmsgcolor'),
- }
-
- cfg_was_read = parser.read()
- gajim.logger.reset_shown_unread_messages()
- # override logging settings from config (don't take care of '-q' option)
- if gajim.config.get('verbose'):
- logging_helpers.set_verbose()
-
- # Is Gajim default app?
- if os.name != 'nt' and gajim.config.get('check_if_gajim_is_default'):
- gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
-
- for account in gajim.config.get_per('accounts'):
- if gajim.config.get_per('accounts', account, 'is_zeroconf'):
- gajim.ZEROCONF_ACC_NAME = account
- break
- # Is gnome configured to activate row on single click ?
- try:
- import gconf
- client = gconf.client_get_default()
- click_policy = client.get_string(
- '/apps/nautilus/preferences/click_policy')
- if click_policy == 'single':
- gajim.single_click = True
- except Exception:
- pass
- # add default status messages if there is not in the config file
- if len(gajim.config.get_per('statusmsg')) == 0:
- 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, 'subactivity',
- 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])
- #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']
-
- default = gajim.config.themes_default
- for theme_name in default:
- gajim.config.add_per('themes', theme_name)
- theme = default[theme_name]
- for o in d:
- gajim.config.set_per('themes', theme_name, o,
- theme[d.index(o)])
-
- if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read:
- gtkgui_helpers.autodetect_browser_mailer()
-
- gajim.idlequeue = idlequeue.get_idlequeue()
- # 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)
- gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
- gajim.default_session_type = ChatControlSession
- self.register_handlers()
- 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)
- for account in gajim.config.get_per('accounts'):
- 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)
-
- # gtk hooks
- gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
- gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
- gtk.link_button_set_uri_hook(self.on_launch_browser_mailer, 'url')
-
- self.instances = {}
-
- 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
- self.minimized_controls[a] = {}
- gajim.contacts.add_account(a)
- gajim.groups[a] = {}
- gajim.gc_connected[a] = {}
- gajim.automatic_rooms[a] = {}
- gajim.newly_added[a] = []
- gajim.to_be_removed[a] = []
- gajim.nicks[a] = gajim.config.get_per('accounts', a, 'name')
- gajim.block_signed_in_notifications[a] = True
- gajim.sleeper_state[a] = 0
- gajim.encrypted_chats[a] = []
- gajim.last_message_time[a] = {}
- gajim.status_before_autoaway[a] = ''
- gajim.transport_avatar[a] = {}
- gajim.gajim_optional_features[a] = []
- gajim.caps_hash[a] = ''
-
- helpers.update_optional_features()
-
- self.remote_ctrl = None
-
- if gajim.config.get('networkmanager_support') and dbus_support.supported:
- import network_manager_listener
-
- # Handle gnome screensaver
- if dbus_support.supported:
- def gnome_screensaver_ActiveChanged_cb(active):
- if not active:
- for account in gajim.connections:
- if gajim.sleeper_state[account] == 'autoaway-forced':
- # 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.sleeper_state[account] = 'online'
- return
- if not gajim.config.get('autoaway'):
- # Don't go auto away if user disabled the option
- return
- for account in gajim.connections:
- if account not in gajim.sleeper_state or \
- not gajim.sleeper_state[account]:
- continue
- if gajim.sleeper_state[account] == 'online':
- # 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]
- 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 % {
- 'status': gajim.status_before_autoaway[account],
- 'time': gajim.config.get('autoxatime')
- }
- self.roster.send_status(account, 'away', auto_message,
- auto=True)
- gajim.sleeper_state[account] = 'autoaway-forced'
-
- try:
- bus = dbus.SessionBus()
- bus.add_signal_receiver(gnome_screensaver_ActiveChanged_cb,
- '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)
-
- gtkgui_helpers.make_jabber_state_images()
-
- self.systray_enabled = False
- self.systray_capabilities = False
-
- if (os.name == 'nt'):
- import statusicon
- self.systray = statusicon.StatusIcon()
- self.systray_capabilities = True
- else: # use ours, not GTK+ one
- # [FIXME: remove this when we migrate to 2.10 and we can do
- # cool tooltips somehow and (not dying to keep) animation]
- import systray
- self.systray_capabilities = systray.HAS_SYSTRAY_CAPABILITIES
- if self.systray_capabilities:
- self.systray = systray.Systray()
-
- path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png')
- pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
- # set the icon to all windows
- gtk.window_set_default_icon(pix)
-
- self.init_emoticons()
- self.make_regexps()
-
- # get transports type from DB
- gajim.transport_type = gajim.logger.get_transports_type()
-
- # test is dictionnary is present for speller
- if gajim.config.get('use_speller'):
- lang = gajim.config.get('speller_language')
- if not lang:
- lang = gajim.LANG
- tv = gtk.TextView()
- try:
- import gtkspell
- spell = gtkspell.Spell(tv, lang)
- except (ImportError, TypeError, RuntimeError, OSError):
- dialogs.AspellDictError(lang)
-
- if gajim.config.get('soundplayer') == '':
- # only on first time Gajim starts
- commands = ('aplay', 'play', 'esdplay', 'artsplay', 'ossplay')
- for command in commands:
- if helpers.is_in_path(command):
- if command == 'aplay':
- command += ' -q'
- gajim.config.set('soundplayer', command)
- break
-
- self.last_ftwindow_update = 0
-
- self.music_track_changed_signal = None
-
-
-class ThreadInterface:
- def __init__(self, func, func_args, callback, callback_args):
- '''Call a function in a thread
-
- :param func: the function to call in the thread
- :param func_args: list or arguments for this function
- :param callback: callback to call once function is finished
- :param callback_args: list of arguments for this callback
- '''
- def thread_function(func, func_args, callback, callback_args):
- output = func(*func_args)
- gobject.idle_add(callback, output, *callback_args)
- Thread(target=thread_function, args=(func, func_args, callback,
- callback_args)).start()
-
-
-class PassphraseRequest:
- def __init__(self, keyid):
- self.keyid = keyid
- self.callbacks = []
- self.dialog_created = False
- self.dialog = None
- self.completed = False
-
- def interrupt(self):
- self.dialog.window.destroy()
- self.callbacks = []
-
- def run_callback(self, account, callback):
- gajim.connections[account].gpg_passphrase(self.passphrase)
- callback()
-
- def add_callback(self, account, cb):
- if self.completed:
- self.run_callback(account, cb)
- else:
- self.callbacks.append((account, cb))
- if not self.dialog_created:
- self.create_dialog(account)
-
- def complete(self, passphrase):
- self.passphrase = passphrase
- self.completed = True
- if passphrase is not None:
- 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
-
- 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}
-
- def _cancel():
- # user cancelled, continue without GPG
- self.complete(None)
-
- def _ok(passphrase, checked, count):
- result = gajim.connections[account].test_gpg_passphrase(passphrase)
- if result == 'ok':
- # passphrase is good
- self.complete(passphrase)
- return
- elif result == 'expired':
- dialogs.ErrorDialog(_('GPG key expired'),
- _('Your GPG key has expied, 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)
- return
-
- 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)
- 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_created = True
\ No newline at end of file
diff --git a/src/interface_test.py b/src/interface_test.py
deleted file mode 100644
index 76751f096..000000000
--- a/src/interface_test.py
+++ /dev/null
@@ -1,88 +0,0 @@
-'''
-Created on Oct 18, 2009
-
-@author: stephan
-'''
-import unittest
-
-import common.configpaths
-common.configpaths.gajimpaths.init(None)
-common.configpaths.gajimpaths.init_profile("tmp-test-profile")
-
-from common import logging_helpers
-logging_helpers.set_quiet()
-
-
-from interface import Interface
-
-class Test(unittest.TestCase):
-
-
- def test_instantiation(self):
- ''' Test that we can proper initialize and do not fail on globals '''
- interface = Interface()
- interface.run()
-
- def test_dispatch(self):
- ''' Test dispatcher forwarding network events to handler_* methods '''
- sut = Interface()
-
- success = sut.dispatch('No Such Event', None, None)
- self.assertFalse(success, msg="Unexisting event handled")
-
- success = sut.dispatch('STANZA_ARRIVED', None, None)
- self.assertTrue(success, msg="Existing event must be handled")
-
- def test_register_unregister_single_handler(self):
- ''' Register / Unregister a custom event handler '''
- sut = Interface()
- event = 'TESTS_ARE_COOL_EVENT'
-
- self.called = False
- def handler(account, data):
- self.assertEqual(account, 'account')
- self.assertEqual(data, 'data')
- self.called = True
-
- self.assertFalse(self.called)
- sut.register_handler('TESTS_ARE_COOL_EVENT', handler)
- sut.dispatch(event, 'account', 'data')
- self.assertTrue(self.called, msg="Handler should have been called")
-
- self.called = False
- sut.unregister_handler('TESTS_ARE_COOL_EVENT', handler)
- sut.dispatch(event, 'account', 'data')
- self.assertFalse(self.called, msg="Handler should no longer be called")
-
-
- def test_dispatch_to_multiple_handlers(self):
- ''' Register and dispatch a single event to multiple handlers '''
- sut = Interface()
- event = 'SINGLE_EVENT'
-
- self.called_a = False
- self.called_b = False
-
- def handler_a(account, data):
- self.assertFalse(self.called_a, msg="One must only be notified once")
- self.called_a = True
-
- def handler_b(account, data):
- self.assertFalse(self.called_b, msg="One must only be notified once")
- self.called_b = True
-
- sut.register_handler(event, handler_a)
- sut.register_handler(event, handler_b)
-
- # register again
- sut.register_handler('SOME_OTHER_EVENT', handler_b)
- sut.register_handler(event, handler_a)
-
- sut.dispatch(event, 'account', 'data')
- self.assertTrue(self.called_a and self.called_b,
- msg="Both handlers should have been called")
-
-
-if __name__ == "__main__":
- #import sys;sys.argv = ['', 'Test.test']
- unittest.main()
\ No newline at end of file