From 3a98a4170c6c13fb20115f79e1f0a2680304e69c Mon Sep 17 00:00:00 2001 From: Stephan Erb Date: Tue, 3 Nov 2009 22:14:19 +0100 Subject: [PATCH] Move Interface() god class from gajim.py to gui_interface.py. --- src/gajim.py | 3399 +---------------- test/runtests.py | 3 +- ...rface.py => test_gui_event_integration.py} | 28 - test/test_gui_interface.py | 111 + 4 files changed, 116 insertions(+), 3425 deletions(-) rename test/{test_misc_interface.py => test_gui_event_integration.py} (83%) create mode 100644 test/test_gui_interface.py diff --git a/src/gajim.py b/src/gajim.py index 81479c6ba..a2e58f4e7 100644 --- a/src/gajim.py +++ b/src/gajim.py @@ -226,36 +226,9 @@ if not hasattr(gobject, 'timeout_add_seconds'): 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 -from common import caps gajimpaths = common.configpaths.gajimpaths @@ -264,8 +237,8 @@ config_filename = gajimpaths['CONFIG_FILE'] import traceback import errno - import dialogs + def pid_alive(): try: pf = open(pid_filename) @@ -394,3374 +367,8 @@ def on_exit(): import atexit atexit.register(on_exit) - -parser = optparser.OptionsParser(config_filename) - -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 expired, you will be connected to %s without' - ' OpenPGP.') % account) - # Don't try to connect with GPG - gajim.connections[account].continue_connect_info[2] = False - self.complete(None) - 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) - # send currently played music - if gajim.connections[account].pep_supported and dbus_support.supported \ - and gajim.config.get_per('accounts', account, 'publish_tune'): - self.enable_music_listener() - - 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) - proposed_resource = gajim.connections[account].server_resource - proposed_resource += gajim.config.get('gc_proposed_nick_char') - dlg = dialogs.ResourceConflictDialog(_('Resource Conflict'), - _('You are already connected to this account with the same resource. ' - 'Please type a new one'), resource=proposed_resource, ok_handler=on_ok) - - def handle_event_jingle_incoming(self, account, data): - # ('JINGLE_INCOMING', account, peer jid, sid, tuple-of-contents==(type, - # data...)) - # TODO: conditional blocking if peer is not in roster - - # unpack data - peerjid, sid, contents = data - content_types = set(c[0] for c in contents) - - # check type of jingle session - if 'audio' in content_types or 'video' in content_types: - # a voip session... - # we now handle only voip, so the only thing we will do here is - # not to return from function - pass - else: - # unknown session type... it should be declined in common/jingle.py - return - - jid = gajim.get_jid_without_resource(peerjid) - resource = gajim.get_resource_from_jid(peerjid) - ctrl = self.msg_win_mgr.get_control(peerjid, account) - if not ctrl: - ctrl = self.msg_win_mgr.get_control(jid, account) - if ctrl: - if 'audio' in content_types: - ctrl.set_audio_state('connection_received', sid) - if 'video' in content_types: - ctrl.set_video_state('connection_received', sid) - - dlg = dialogs.VoIPCallReceivedDialog.get_dialog(peerjid, sid) - if dlg: - dlg.add_contents(content_types) - return - - if helpers.allow_popup_window(account): - dialogs.VoIPCallReceivedDialog(account, peerjid, sid, content_types) - return - - self.add_event(account, peerjid, 'jingle-incoming', (peerjid, sid, - content_types)) - - if helpers.allow_showing_notification(account): - # TODO: we should use another pixmap ;-) - img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', - 'ft_request.png') - txt = _('%s wants to start a voice chat.') % gajim.get_name_from_jid( - account, peerjid) - path = gtkgui_helpers.get_path_to_generic_or_avatar(img) - event_type = _('Voice Chat Request') - notify.popup(event_type, peerjid, account, 'jingle-incoming', - path_to_image = path, title = event_type, text = txt) - - def handle_event_jingle_connected(self, account, data): - # ('JINGLE_CONNECTED', account, (peerjid, sid, media)) - peerjid, sid, media = data - if media in ('audio', 'video'): - jid = gajim.get_jid_without_resource(peerjid) - resource = gajim.get_resource_from_jid(peerjid) - ctrl = self.msg_win_mgr.get_control(peerjid, account) - if not ctrl: - ctrl = self.msg_win_mgr.get_control(jid, account) - if ctrl: - if media == 'audio': - ctrl.set_audio_state('connected', sid) - else: - ctrl.set_video_state('connected', sid) - - def handle_event_jingle_disconnected(self, account, data): - # ('JINGLE_DISCONNECTED', account, (peerjid, sid, reason)) - peerjid, sid, media, reason = data - jid = gajim.get_jid_without_resource(peerjid) - resource = gajim.get_resource_from_jid(peerjid) - ctrl = self.msg_win_mgr.get_control(peerjid, account) - if not ctrl: - ctrl = self.msg_win_mgr.get_control(jid, account) - if ctrl: - if media in ('audio', None): - ctrl.set_audio_state('stop', sid=sid, reason=reason) - if media in ('video', None): - ctrl.set_video_state('stop', sid=sid, reason=reason) - dialog = dialogs.VoIPCallReceivedDialog.get_dialog(peerjid, sid) - if dialog: - dialog.dialog.destroy() - - def handle_event_jingle_error(self, account, data): - # ('JINGLE_ERROR', account, (peerjid, sid, reason)) - peerjid, sid, reason = data - jid = gajim.get_jid_without_resource(peerjid) - resource = gajim.get_resource_from_jid(peerjid) - ctrl = self.msg_win_mgr.get_control(peerjid, account) - if not ctrl: - ctrl = self.msg_win_mgr.get_control(jid, account) - if ctrl: - ctrl.set_audio_state('error', reason=reason) - - 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, - 'JINGLE_INCOMING': self.handle_event_jingle_incoming, - 'JINGLE_CONNECTED': self.handle_event_jingle_connected, - 'JINGLE_DISCONNECTED': self.handle_event_jingle_disconnected, - 'JINGLE_ERROR': self.handle_event_jingle_error, - } - - 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 jingle-incoming - # 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) - elif type_ == 'jingle-incoming': - event = gajim.events.get_first_event(account, jid, type_) - peerjid, sid, content_types = event.parameters - dialogs.VoIPCallReceivedDialog(account, peerjid, sid, content_types) - gajim.events.remove_events(account, jid, event) - 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) - - 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'), - 'inmsgtxtcolor': gajim.config.get('inmsgtxtcolor'), - 'outmsgtxtcolor': gajim.config.get('outmsgtxtcolor'), - '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() - # prepopulate data which we are sure of; note: we do not log these info - for account in gajim.connections: - gajimcaps = caps.capscache[('sha-1', gajim.caps_hash[account])] - gajimcaps.identities = [gajim.gajim_identity] - gajimcaps.features = gajim.gajim_common_features + \ - gajim.gajim_optional_features[account] - - 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() - else: - gajim.config.set('trayicon', 'never') - - 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 +from gui_interface import Interface if __name__ == '__main__': def sigint_cb(num, stack): diff --git a/test/runtests.py b/test/runtests.py index fa95009f4..bb7e86191 100755 --- a/test/runtests.py +++ b/test/runtests.py @@ -41,11 +41,12 @@ modules = ( 'test_xmpp_dispatcher_nb', 'test_resolver', 'test_caps', 'test_contacts', + 'test_gui_interface', ) #modules = () if use_x: - modules += ('test_misc_interface', + modules += ('test_gui_event_integration', 'test_roster', 'test_sessions', ) diff --git a/test/test_misc_interface.py b/test/test_gui_event_integration.py similarity index 83% rename from test/test_misc_interface.py rename to test/test_gui_event_integration.py index 4e31b35dc..00ed5c696 100644 --- a/test/test_misc_interface.py +++ b/test/test_gui_event_integration.py @@ -14,34 +14,6 @@ gajim.logger = MockLogger() Interface() -class TestMiscInterface(unittest.TestCase): - - def test_links_regexp_entire(self): - def assert_matches_all(str_): - m = gajim.interface.basic_pattern_re.match(str_) - - # the match should equal the string - str_span = (0, len(str_)) - self.assertEqual(m.span(), str_span) - - # these entire strings should be parsed as links - assert_matches_all('http://google.com/') - assert_matches_all('http://google.com') - assert_matches_all('http://www.google.ca/search?q=xmpp') - - assert_matches_all('http://tools.ietf.org/html/draft-saintandre-rfc3920bis-05#section-12.3') - - assert_matches_all('http://en.wikipedia.org/wiki/Protocol_(computing)') - assert_matches_all( - 'http://en.wikipedia.org/wiki/Protocol_%28computing%29') - - assert_matches_all('mailto:test@example.org') - - assert_matches_all('xmpp:example-node@example.com') - assert_matches_all('xmpp:example-node@example.com/some-resource') - assert_matches_all('xmpp:example-node@example.com?message') - assert_matches_all('xmpp://guest@example.com/support@example.com?message') - import time from data import * diff --git a/test/test_gui_interface.py b/test/test_gui_interface.py new file mode 100644 index 000000000..b2acc019f --- /dev/null +++ b/test/test_gui_interface.py @@ -0,0 +1,111 @@ +import unittest + +import lib +lib.setup_env() + +from gajim_mocks import * +gajim.logger = MockLogger() + +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") + + +def test_links_regexp_entire(self): + sut = Interface() + def assert_matches_all(str_): + m = sut.basic_pattern_re.match(str_) + + # the match should equal the string + str_span = (0, len(str_)) + self.assertEqual(m.span(), str_span) + + # these entire strings should be parsed as links + assert_matches_all('http://google.com/') + assert_matches_all('http://google.com') + assert_matches_all('http://www.google.ca/search?q=xmpp') + + assert_matches_all('http://tools.ietf.org/html/draft-saintandre-rfc3920bis-05#section-12.3') + + assert_matches_all('http://en.wikipedia.org/wiki/Protocol_(computing)') + assert_matches_all( + 'http://en.wikipedia.org/wiki/Protocol_%28computing%29') + + assert_matches_all('mailto:test@example.org') + + assert_matches_all('xmpp:example-node@example.com') + assert_matches_all('xmpp:example-node@example.com/some-resource') + assert_matches_all('xmpp:example-node@example.com?message') + assert_matches_all('xmpp://guest@example.com/support@example.com?message') + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.test'] + unittest.main() \ No newline at end of file