From ea24ee82e56936fa2d31fb3afb3e09901224db75 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Thu, 9 Aug 2007 15:39:18 +0000 Subject: [PATCH] merge diff from trunk to pep branch --- AUTHORS | 2 +- ChangeLog | 12 +- configure.ac | 2 +- data/glade/account_context_menu.glade | 294 +- .../account_creation_wizard_window.glade | 287 +- data/glade/accounts_window.glade | 1654 ++++- data/glade/add_new_contact_window.glade | 850 +-- .../glade/advanced_notifications_window.glade | 2124 +++---- data/glade/chat_control_popup_menu.glade | 10 - data/glade/features_window.glade | 116 + data/glade/filetransfers.glade | 669 +- data/glade/gajim_themes_window.glade | 2 +- data/glade/gc_control_popup_menu.glade | 40 +- data/glade/gc_occupants_menu.glade | 47 +- data/glade/history_manager.glade | 627 +- data/glade/manage_bookmarks_window.glade | 818 +-- data/glade/message_window.glade | 2 +- data/glade/preferences_window.glade | 5520 +++++++---------- data/glade/privacy_list_window.glade | 1273 ++-- data/glade/profile_window.glade | 3095 ++++----- data/glade/roster_contact_context_menu.glade | 16 +- data/glade/roster_window.glade | 30 +- data/glade/search_window.glade | 402 +- data/glade/service_discovery_window.glade | 684 +- data/glade/single_message_window.glade | 890 +-- data/glade/vcard_information_window.glade | 4361 +++++-------- data/glade/xml_console_window.glade | 4 +- .../glade/zeroconf_contact_context_menu.glade | 262 +- .../crystal/16x16/{message.gif => event.gif} | Bin .../dcraven/16x16/{message.png => event.png} | Bin .../dcraven/32x32/{message.png => event.png} | Bin .../gnome/16x16/{message.gif => event.gif} | Bin .../goojim/16x16/{message.png => event.png} | Bin .../goojim/32x32/{message.png => event.png} | Bin .../gossip/16x16/{message.png => event.png} | Bin .../gota/16x16/{message.gif => event.gif} | Bin .../gota/32x32/{message.gif => event.gif} | Bin .../16x16/{message.gif => event.gif} | Bin .../nuvola/16x16/{message.gif => event.gif} | Bin .../16x16/{message.png => event.png} | Bin .../stellar/16x16/{message.gif => event.gif} | Bin .../sun/16x16/{message.gif => event.gif} | Bin data/other/cacerts.pem | 2474 ++++++++ po/de.po | 60 +- po/ru.po | 461 +- src/adhoc_commands.py | 56 +- src/chat_control.py | 244 +- src/common/atom.py | 12 +- src/common/caps.py | 259 + src/common/check_paths.py | 7 + src/common/commands.py | 32 +- src/common/config.py | 14 +- src/common/configpaths.py | 77 +- src/common/connection.py | 229 +- src/common/connection_handlers.py | 274 +- src/common/contacts.py | 58 +- src/common/dataforms.py | 8 +- src/common/dbus_support.py | 2 + src/common/defs.py | 2 +- src/common/events.py | 2 +- src/common/exceptions.py | 9 + src/common/gajim.py | 1 + src/common/helpers.py | 199 +- src/common/logger.py | 111 +- src/common/optparser.py | 96 +- src/common/passwords.py | 16 +- src/common/pubsub.py | 2 +- src/common/socks5.py | 47 +- src/common/xmpp/commands.py | 2 +- src/common/xmpp/dispatcher_nb.py | 5 +- src/common/xmpp/features_nb.py | 13 +- src/common/xmpp/protocol.py | 70 +- src/common/xmpp/transports_nb.py | 70 +- src/common/zeroconf/client_zeroconf.py | 24 +- .../zeroconf/connection_handlers_zeroconf.py | 14 +- src/common/zeroconf/connection_zeroconf.py | 16 +- src/common/zeroconf/zeroconf.py | 415 +- src/common/zeroconf/zeroconf_avahi.py | 415 ++ src/common/zeroconf/zeroconf_bonjour.py | 329 + src/config.py | 1529 +++-- src/conversation_textview.py | 48 +- src/dialogs.py | 112 +- src/disco.py | 88 +- src/features_window.py | 276 + src/filetransfers_window.py | 27 +- src/gajim-remote.py | 9 + src/gajim.py | 252 +- src/gajim_themes_window.py | 39 +- src/groupchat_control.py | 330 +- src/gtkgui_helpers.py | 5 +- src/history_manager.py | 5 +- src/message_control.py | 12 +- src/message_window.py | 43 +- src/music_track_listener.py | 9 +- src/network_manager_listener.py | 34 +- src/notify.py | 22 +- src/profile_window.py | 4 - src/remote_control.py | 48 +- src/roster_window.py | 1127 ++-- src/search_window.py | 152 +- src/statusicon.py | 3 +- src/systray.py | 18 +- src/tooltips.py | 31 +- src/vcard.py | 142 +- 104 files changed, 17800 insertions(+), 16783 deletions(-) create mode 100644 data/glade/features_window.glade rename data/iconsets/crystal/16x16/{message.gif => event.gif} (100%) rename data/iconsets/dcraven/16x16/{message.png => event.png} (100%) rename data/iconsets/dcraven/32x32/{message.png => event.png} (100%) rename data/iconsets/gnome/16x16/{message.gif => event.gif} (100%) rename data/iconsets/goojim/16x16/{message.png => event.png} (100%) rename data/iconsets/goojim/32x32/{message.png => event.png} (100%) rename data/iconsets/gossip/16x16/{message.png => event.png} (100%) rename data/iconsets/gota/16x16/{message.gif => event.gif} (100%) rename data/iconsets/gota/32x32/{message.gif => event.gif} (100%) rename data/iconsets/jabberbulb/16x16/{message.gif => event.gif} (100%) rename data/iconsets/nuvola/16x16/{message.gif => event.gif} (100%) rename data/iconsets/simplebulb/16x16/{message.png => event.png} (100%) rename data/iconsets/stellar/16x16/{message.gif => event.gif} (100%) rename data/iconsets/sun/16x16/{message.gif => event.gif} (100%) create mode 100644 data/other/cacerts.pem create mode 100644 src/common/caps.py mode change 100755 => 100644 src/common/zeroconf/zeroconf.py create mode 100644 src/common/zeroconf/zeroconf_avahi.py create mode 100644 src/common/zeroconf/zeroconf_bonjour.py create mode 100644 src/features_window.py diff --git a/AUTHORS b/AUTHORS index 3688a1c43..9c646612b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,10 +3,10 @@ CURRENT DEVELOPERS: Yann Le Boulanger (asterix AT lagaule.org) Nikos Kouremenos (kourem AT gmail.com) Travis Shirk (travis AT pobox.com) -Jean-Marie Traissard (jim AT lapin.org) PAST DEVELOPERS: +Jean-Marie Traissard (jim AT lapin.org) Stefan Bethge (stefan AT lanpartei.de) Dimitur Kirov (dkirov AT gmail.com) Vincent Hanquez (tab AT snarc.org) diff --git a/ChangeLog b/ChangeLog index de8783118..a4fba5334 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ -Gajim 0.11.1 (XX February 2007) +Gajim 0.11.1 (18 February 2007) + * Fixes in gajim-remote and the way XMPP URI are handled + * Fix Idle under Windows + * Fix Gajim under non-ascii languages Windows + * Fix International Domain Name usage + * Fix when removing active privacy list + * Fix problem with adhoc command and multi-step forms * Fixed avatars cache problems in group chats + * KDE integration for XMPP URI + * Support of Banshee Music player + * Support of XEP-0202 (Entity Time) + * Support of XEP-0199 (XMPP Ping) Gajim 0.11 (19 December 2006) * New build system, using GNU autotools. See README.html diff --git a/configure.ac b/configure.ac index abd261585..c4bdb142e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT([Gajim - A Jabber Instant Messager], - [0.11.1.0],[http://trac.gajim.org/],[gajim]) + [0.11.1.5],[http://trac.gajim.org/],[gajim]) AC_PREREQ([2.59]) AM_INIT_AUTOMAKE([1.8]) AC_CONFIG_HEADER(config.h) diff --git a/data/glade/account_context_menu.glade b/data/glade/account_context_menu.glade index 900db4ff8..ef05651e6 100644 --- a/data/glade/account_context_menu.glade +++ b/data/glade/account_context_menu.glade @@ -1,177 +1,121 @@ - - - + + + - - - - - - True - _Status - True - - - - True - gtk-network - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Personal Events - True - - - - True - gtk-home - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Group Chat - True - - - - True - gtk-connect - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Open Gmail Inbox - True - - - - - - True - Send Single _Message... - True - - - - True - gtk-new - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Add Contact... - True - - - - True - gtk-add - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Discover Services... - True - - - - True - gtk-find - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Execute Command... - True - - - - True - gtk-execute - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - _Modify Account... - True - - - - True - gtk-preferences - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - + + + + True + _Status + True + + + True + gtk-network + 1 + + + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Personal Events + True + + + gtk-home + + + + + + + True + + + + + True + _Open Gmail Inbox + True + + + + + True + _Group Chat + True + + + True + gtk-connect + 1 + + + + + + + True + _Discover Services + True + + + True + gtk-find + 1 + + + + + + + True + _Execute Command + True + + + True + gtk-execute + 1 + + + + + + + True + + + + + True + _Add Contact + True + + + True + gtk-add + 1 + + + + + + + True + _Modify Account... + True + + + True + gtk-preferences + 1 + + + + + diff --git a/data/glade/account_creation_wizard_window.glade b/data/glade/account_creation_wizard_window.glade index 50d61e400..93ba1cb8b 100644 --- a/data/glade/account_creation_wizard_window.glade +++ b/data/glade/account_creation_wizard_window.glade @@ -53,6 +53,7 @@ to the Jabber network. True I already have an account I want to use True + 0 True @@ -66,6 +67,7 @@ to the Jabber network. True I want to _register for a new account True + 0 True use_existing_account_radiobutton @@ -142,96 +144,30 @@ to the Jabber network. - + True True - 0 - True - True - - - 1 - 3 - 3 - 4 - GTK_FILL - - - - - - True - 0 - Your JID: - - - 3 - 4 - GTK_FILL - - - - - - True - 0 - _Username: - True - username_entry - - - GTK_FILL - - - - - - True - True - False + True * - True + + 1 2 - 2 - 3 - - True - True - If checked, Gajim will remember the password for this account - Save pass_word - True - False - True - True - - - - 2 - 3 - 2 - 3 - GTK_FILL - - - - - + True 0 - _Password: + _Server: True - password_entry - 2 - 3 + 1 + 2 GTK_FILL @@ -255,31 +191,98 @@ to the Jabber network. - + True 0 - _Server: + _Password: True + password_entry - 1 - 2 + 2 + 3 GTK_FILL - + True True - True + If checked, Gajim will remember the password for this account + Save pass_word + True + False + 0 + True + True + + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + True + False * - - + True 1 2 + 2 + 3 + + + + + + True + 0 + _Username: + True + username_entry + + + GTK_FILL + + + + + + True + 0 + Your JID: + + + 3 + 4 + GTK_FILL + + + + + + True + True + 0 + True + True + + + 1 + 3 + 3 + 4 + GTK_FILL @@ -357,6 +360,7 @@ to the Jabber network. Click to see features (like MSN, ICQ transports) of jabber servers Servers Features True + 0 @@ -384,6 +388,62 @@ to the Jabber network. 3 5 5 + + + True + 0 + Prox_y: + True + + + GTK_FILL + + + + + + True + None + + + 1 + 2 + GTK_FILL + + + + + True + True + Manage... + True + 0 + + + + 2 + 3 + + + + + + + True + True + Use custom hostname/port + True + 0 + True + + + + 3 + 1 + 2 + + + True @@ -446,60 +506,6 @@ to the Jabber network. GTK_FILL - - - True - True - Use custom hostname/port - True - True - - - - 3 - 1 - 2 - - - - - - True - True - Manage... - True - - - - 2 - 3 - - - - - - - True - None - - - 1 - 2 - GTK_FILL - - - - - True - 0 - Prox_y: - True - - - GTK_FILL - - - @@ -637,6 +643,7 @@ Please wait... True Connect when I press Finish True + 0 True True @@ -652,6 +659,7 @@ Please wait... True Set my profile when I connect True + 0 True True @@ -692,6 +700,7 @@ Please wait... True gtk-cancel True + 0 @@ -703,6 +712,7 @@ Please wait... True gtk-go-back True + 0 @@ -717,6 +727,7 @@ Please wait... True gtk-go-forward True + 0 @@ -727,6 +738,8 @@ Please wait... True True + True + 0 @@ -772,6 +785,8 @@ Please wait... True True + True + 0 diff --git a/data/glade/accounts_window.glade b/data/glade/accounts_window.glade index bd3b2314e..ecd00cea6 100644 --- a/data/glade/accounts_window.glade +++ b/data/glade/accounts_window.glade @@ -1,305 +1,1353 @@ - - - + + + - - - 12 - Accounts - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 150 - True - False - accounts - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 6 - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_NEVER - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - True - False - False - False - - - - - - - 0 - True - True - - - - - - True - If you have 2 or more accounts and this is checked, Gajim will list all contacts as if you had one account - True - Mer_ge accounts - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - - - 0 - False - True - - - - - - True - If checked, all local contacts that use a Bonjour compatible chat client (like iChat, Trillian or Gaim) will be shown in roster. You don't need to be connected to a jabber server for it to work. -This is only available if python-avahi is installed and avahi-daemon is running. - True - _Enable link-local messaging - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - GTK_BUTTONBOX_END - 6 - - - - True - gtk-new - True - GTK_RELIEF_NORMAL - True - - - - - - - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-preferences - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Modify - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-delete - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Remove - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - True - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - - + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + 600 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 175 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + True + + + + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-add + True + + + + False + 1 + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-remove + True + + + + False + 2 + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-missing-image + + + False + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Re_name + True + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + 3 + + + + + + + False + 3 + + + + + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + + + + + + tab + + + + + True + True + + + True + 6 + 5 + 3 + 6 + 6 + + + True + True + Resource is sent to the Jabber server in order to separate the same JID in two or more parts depending on the number of the clients connected in the same server with the same account. So you might be connected in the same account with resource 'Home' and 'Work' at the same time. The resource which has the highest priority will get the events. (see below) + * + Gajim + + + + 1 + 3 + 2 + 3 + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + + True + True + * + True + + + + 1 + 3 + + + + + + True + True + + + True + 11 + True + + + True + True + Click to request authorization to all contacts of another account + Synchronise contacts + True + + + + False + + + + + True + True + Click to change account's password + Chan_ge Password + True + + + + False + 1 + + + + + + + True + Administration operations + + + label_item + + + + + 3 + 4 + 5 + GTK_FILL + GTK_FILL + + + + + True + True + Priority is used in Jabber to determine who gets the events from the jabber server when two or more clients are connected using the same account; The client with the highest priority gets the events + 5 0 127 1 5 5 + 1 + True + + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + True + True + Priority will change automatically according to your status. + _Adjust to status + True + True + + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + 0 + Priori_ty: + True + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + Resour_ce: + True + + + 2 + 3 + GTK_FILL + + + + + + True + True + If checked, Gajim will remember the password for this account + Save pass_word + True + False + True + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + False + True + False + * + True + + + + 1 + 2 + 1 + 2 + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + + True + 0 + _Password: + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Jabber ID: + True + + + GTK_FILL + + + + + + False + + + + + True + Account + + + tab + False + False + + + + + True + 6 + 6 + + + True + True + If checked, Gajim, when launched, will automatically connect to jabber using this account + C_onnect on Gajim startup + True + True + + + + False + False + + + + + True + True + Auto-reconnect when connection is lost + True + True + + + + False + False + 1 + + + + + True + True + Save conversation _logs for all contacts + True + True + True + + + + False + False + 2 + + + + + True + True + If checked, any change to the global status (handled by the combobox at the bottom of the roster window) will change the status of this account accordingly + Synch_ronize account status with global status + True + True + + + + False + False + 3 + + + + + True + True + If checked, Gajim will also broadcast some more IPs except from just your IP, so file transfer has higher chances of working. + Use file transfer proxies + True + True + + + + False + False + 4 + + + + + 1 + False + + + + + True + General + True + + + tab + 1 + False + False + + + + + True + 6 + 12 + + + True + 6 + 6 + + + True + Proxy: + + + False + False + + + + + True + None + + + + 1 + + + + + True + True + Manage... + True + + + + False + False + 2 + + + + + False + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 6 + 6 + + + True + True + Check this so Gajim will connect in port 5223 where legacy servers are expected to have SSL capabilities. Note that Gajim uses TLS encryption by default if broadcasted by the server, and with this option enabled TLS will be disabled + Use _SSL (legacy) + True + True + + + + False + False + + + + + True + True + If checked, Gajim will send keep-alive packets to prevent connection timeout which results in disconnection + Send keep-alive packets + True + True + True + + + + False + False + 1 + + + + + True + True + Use custom hostname/port + True + True + + + + False + False + 2 + + + + + True + False + 6 + + + True + Hostname: + + + False + False + + + + + True + True + * + + + + 1 + + + + + True + Port: + + + False + False + 2 + + + + + True + True + * + 6 + 5222 + + + + False + 3 + + + + + 3 + + + + + + + + + True + <b>Miscellaneous</b> + True + + + label_item + + + + + False + 1 + + + + + 2 + False + + + + + True + Connection + + + tab + 2 + False + False + + + + + True + 5 + 6 + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 6 + 6 + + + True + 6 + + + True + True + No key selected + True + + + False + False + + + + + True + True + True + + + 1 + + + + + True + True + Choose _Key... + True + + + + False + False + 2 + + + + + False + + + + + True + 6 + + + True + False + True + If checked, Gajim will store the password in ~/.gajim/config with 'read' permission only for you + Save _passphrase (insecure) + True + True + + + + False + False + + + + + True + False + True + False + * + + + + 1 + + + + + False + 1 + + + + + + + + + True + <b>OpenPGP</b> + True + + + label_item + + + + + False + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + True + Information about you, as stored in the server + Edit Personal Information... + True + + + + + + + + True + <b>Personal Information</b> + True + + + label_item + + + + + False + 1 + + + + + 3 + False + + + + + True + Personal Information + + + tab + 3 + False + False + + + + + 1 + False + + + + + + tab + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enable + True + + + + False + + + + + True + True + + + True + 6 + 6 + + + True + True + If checked, Gajim, when launched, will automatically connect to jabber using this account + C_onnect on Gajim startup + True + True + + + + False + False + + + + + True + True + Save conversation _logs for all contacts + True + True + + + + False + False + 1 + + + + + True + True + If checked, any change to the global status (handled by the combobox at the bottom of the roster window) will change the status of this account accordingly + Synch_ronize account status with global status + True + True + + + + False + False + 2 + + + + + True + + + True + True + If the default port that is used for incoming messages is unfitting for your setup you can select another one here. +You might consider to change possible firewall settings. + Use custom port: + True + True + + + + False + False + + + + + True + True + 6 + + + + False + False + 1 + + + + + False + 10 + 3 + + + + + False + + + + + True + General + True + + + tab + False + False + + + + + True + 6 + 6 + 2 + 2 + 5 + + + True + True + + + + 1 + 2 + 4 + 5 + + + + + + True + Jabber ID: + + + 4 + 5 + GTK_FILL + + + + + + True + E-Mail: + + + 5 + 6 + GTK_FILL + + + + + + True + True + + + + 1 + 2 + 5 + 6 + + + + + + True + Last Name: + + + 3 + 4 + GTK_FILL + + + + + + True + True + + + + 1 + 2 + 3 + 4 + + + + + + True + First Name: + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + + 1 + 2 + 2 + 3 + + + + + + True + 0 + <b>Personal Information</b> + True + + + 2 + 1 + 2 + GTK_FILL + + + + + + True + 5 + + + True + 0 + <b>OpenPGP</b> + True + + + False + False + + + + + True + 6 + + + True + No key selected + + + False + False + + + + + True + + + 1 + + + + + True + True + Choose _Key... + True + + + + False + False + 2 + + + + + 1 + + + + + True + 6 + + + True + True + If checked, Gajim will store the password in ~/.gajim/config with 'read' permission only for you + Save _passphrase (insecure) + True + True + + + + False + False + + + + + True + False + True + False + * + + + + 1 + + + + + 2 + + + + + 2 + GTK_FILL + + + + + 1 + False + + + + + True + Personal Information + + + tab + 1 + False + False + + + + + 1 + + + + + 2 + False + + + + + + tab + + + + + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Mer_ge accounts + True + True + + + + False + 1 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + False + 2 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + GTK_BUTTONBOX_END + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-close + True + + + + + + False + 3 + + + + + diff --git a/data/glade/add_new_contact_window.glade b/data/glade/add_new_contact_window.glade index 3e9bdc8ae..cd10d283e 100644 --- a/data/glade/add_new_contact_window.glade +++ b/data/glade/add_new_contact_window.glade @@ -1,527 +1,335 @@ - - - + + + - - - 6 - Add New Contact - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - False - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 6 - - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - 6 - - - - True - A_ccount: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - - False - True - - - 0 - False - False - - - - - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - _Protocol: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - uid_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - - False - True - - - - 0 - False - False - - - - - - True - - False - True - - - 0 - True - True - - - - - 0 - True - True - - - - - - 6 - True - 3 - 2 - False - 6 - 6 - - - - True - _User ID: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - uid_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - True - True - True - 0 - - True - * - True - - - - 1 - 2 - 0 - 1 - - - - - - - True - _Nickname: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - nickname_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - True - True - True - 0 - - True - * - True - - - 1 - 2 - 1 - 2 - - - - - - - True - _Group: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - - False - True - True - - - 1 - 2 - 2 - 3 - fill - - - - - 0 - False - True - - - - - - True - True - A_llow this contact to view my status - True - GTK_RELIEF_NORMAL - True - True - False - True - - - 0 - False - False - - - - - - 6 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_ETCHED_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - True - 0 - 0 - 0 - 0 - 0 - 0 - I would like to add you to my contact list. - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - You have to register with this transport + + 6 + Add New Contact + False + + + + + True + 6 + + + True + + + False + False + + + + + True + 6 + + + True + 0 + A_ccount: + True + + + False + False + + + + + True + + + + False + False + 1 + + + + + + + + 1 + + + + + True + 6 + + + True + 0 + _Protocol: + True + uid_entry + + + False + False + + + + + True + + + + False + False + 1 + + + + + True + True + + + + 2 + + + + + 2 + + + + + True + True + 6 + 3 + 2 + 6 + 6 + + + True + 0 + _User ID: + True + uid_entry + + + GTK_FILL + + + + + + True + True + * + True + + + + 1 + 2 + + + + + + True + 0 + _Nickname: + True + nickname_entry + + + 1 + 2 + GTK_FILL + + + + + + True + True + * + True + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + _Group: + True + + + 2 + 3 + GTK_FILL + + + + + + True + + + + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + False + 3 + + + + + True + True + A_llow this contact to view my status + True + 0 + True + True + + + False + False + 4 + + + + + True + True + True + 6 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_ETCHED_IN + + + True + True + GTK_WRAP_WORD + I would like to add you to my contact list. + + + + + 5 + + + + + True + True + 6 + + + True + 0 + You have to register with this transport to be able to add a contact from this protocol. Click on register button to proceed. - False - False - GTK_JUSTIFY_LEFT - True - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - True - True - _Register - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - You must be connected to the transport to be able + True + + + + + True + True + True + _Register + True + 0 + + + + False + False + 1 + + + + + 6 + + + + + True + True + You must be connected to the transport to be able to add a contact from this protocol. - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - 5 - True - GTK_BUTTONBOX_END - 12 - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - True - gtk-add - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - False - - - - - - + + + False + False + 7 + + + + + True + 5 + 12 + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + 0 + + + + + + True + True + True + True + gtk-add + True + 0 + + + + 1 + + + + + False + False + 8 + + + + + diff --git a/data/glade/advanced_notifications_window.glade b/data/glade/advanced_notifications_window.glade index 46c78b20c..eed865ceb 100644 --- a/data/glade/advanced_notifications_window.glade +++ b/data/glade/advanced_notifications_window.glade @@ -1,283 +1,188 @@ - - - + + + - - - 6 - Advanced Notifications Control - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - False - True - Advanced Notifications Control - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - 12 - True - False - 12 - - - - True - False - 0 - - - - True - Hi SVN users ! This window does nothing for the moment. Discussions about this take place in ticket #1005. - False - False - GTK_JUSTIFY_LEFT - True - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - 0 - False - False - - - - - - True - False - 5 - - - - 90 - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - True - False - False - False - - - - - - 0 - False - False - - - - - - True - 1 - 0.5 - 1 - 1 - 0 - 0 - 212 - 0 - - - - True - False - 0 - - - - True - GTK_BUTTONBOX_DEFAULT_STYLE - 10 - - - - True - True - True - gtk-new - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-go-up - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-go-down - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-delete - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - True - False - - - - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - False - 5 - - - - True - <b>Conditions</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - 5 - - - - True - False - 5 - - - - True - When - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - Receive a Message + + 6 + Advanced Notifications Control + Advanced Notifications Control + False + True + + + True + 12 + 12 + + + True + + + True + Hi SVN users ! This window does nothing for the moment. Discussions about this take place in ticket #1005. + True + + + False + False + + + + + + + + False + False + + + + + True + 5 + + + 90 + True + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + + + + + + False + False + + + + + True + 1 + 212 + + + True + + + True + 10 + + + True + True + True + gtk-new + True + 0 + + + + + + True + True + True + gtk-go-up + True + 0 + + + + 1 + + + + + True + True + True + gtk-go-down + True + 0 + + + + 2 + + + + + True + True + True + gtk-delete + True + 0 + + + + 3 + + + + + False + + + + + + + False + False + 1 + + + + + 1 + + + + + True + 5 + + + True + <b>Conditions</b> + True + + + False + False + + + + + True + 5 + + + True + 5 + + + True + When + True + + + False + False + + + + + True + Receive a Message Contact Disconnected Contact Change Status Group Chat Message Highlight @@ -285,994 +190,679 @@ Group Chat Message Received File Transfer Request File Transfer Started File Transfer Finished - False - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - False - 5 - - - - True - for - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - contact(s) + + + + False + False + 1 + + + + + + + True + 5 + + + True + for + True + + + False + False + + + + + True + contact(s) group(s) everybody - False - True - - - - 0 - False - True - - - - - - True - True - True - True - 0 - - True - * - False - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - False - 0 - - - - True - when I'm in - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - 3 - - - - True - True - All statuses - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - One or more special statuses... - True - GTK_RELIEF_NORMAL - True - False - False - True - all_status_rb - - - 0 - False - False - - - - - - True - True - Online / Free For Chat - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Away - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Not Available - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Busy - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Invisible - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - False - 0 - - - - True - and I - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - True - Have - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Don't have - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - a window/tab opened with that contact - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - <b>Actions</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - True - False - 6 - - - - True - True - _Inform me with a popup window - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - _Disable existing popup window - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - True - 6 - - - - True - True - Play a sound - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - True - True - - - - - - True - False - False - 6 - - - - True - True - True - True - 0 - - True - * - False - - - - 0 - True - True - - - - - - True - True - ... - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - True - True - GTK_RELIEF_NORMAL - True - - - - - True - gtk-media-play - 4 - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - False - False - - - - - - True - True - _Disable existing sound for this event - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - - - - - True - <b>Sounds</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - label_item - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - True - _Open chat window with user - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - _Disable auto opening chat window - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - True - True - 0 - - - - True - False - 5 - - - - True - False - 6 - - - - True - True - Launch a command - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - True - - - - - - True - False - True - True - True - 0 - - True - * - False - - - - 0 - True - True - - - - - 0 - False - False - - - - - - True - False - 6 - - - - True - True - _Show event in systray - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - _Disable showing event in systray - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - True - _Show event in roster - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - _Disable showing event in roster - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - True - _Activate window manager's UrgencyHint to make chat window in taskbar flash - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - - - True - Advanced Actions - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 413 - 0 - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - - + + + + False + 1 + + + + + True + True + True + * + + + + 2 + + + + + 1 + + + + + True + + + True + when I'm in + True + + + False + False + + + + + True + 3 + + + True + True + All statuses + True + 0 + True + + + + False + False + + + + + True + True + One or more special statuses... + True + 0 + True + all_status_rb + + + False + False + 1 + + + + + True + True + True + Online / Free For Chat + True + 0 + True + + + + False + False + 2 + + + + + True + True + True + Away + True + 0 + True + + + + False + False + 3 + + + + + True + True + True + Not Available + True + 0 + True + + + + False + False + 4 + + + + + True + True + True + Busy + True + 0 + True + + + + False + False + 5 + + + + + True + True + True + Invisible + True + 0 + True + + + + False + False + 6 + + + + + 1 + + + + + 2 + + + + + True + + + True + and I + True + + + False + False + + + + + True + True + Have + True + 0 + True + + + + False + False + 1 + + + + + True + True + Don't have + True + 0 + True + + + + False + False + 2 + + + + + True + a window/tab opened with that contact + True + + + False + False + 3 + + + + + 3 + + + + + 1 + + + + + True + <b>Actions</b> + True + + + False + False + 2 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + + + True + True + _Inform me with a popup window + True + 0 + True + + + + False + False + + + + + True + True + _Disable existing popup window + True + 0 + True + + + + False + False + 1 + + + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + 6 + True + + + True + True + Play a sound + True + 0 + True + + + + + + True + False + 6 + + + True + True + * + + + + + + True + True + ... + True + 0 + + + + False + False + 1 + + + + + True + True + 0 + + + + True + gtk-media-play + + + + + False + False + 2 + + + + + 1 + + + + + False + False + + + + + True + True + _Disable existing sound for this event + True + 0 + True + + + + False + False + 1 + + + + + + + + + True + <b>Sounds</b> + True + + + label_item + + + + + label_item + + + + + 3 + + + + + True + 6 + + + True + True + _Open chat window with user + True + 0 + True + + + + False + False + + + + + True + True + _Disable auto opening chat window + True + 0 + True + + + + False + False + 1 + + + + + 4 + + + + + True + True + True + + + True + 5 + + + True + 6 + + + True + True + Launch a command + True + 0 + True + + + + False + + + + + True + False + True + * + + + + 1 + + + + + False + False + + + + + True + 6 + + + True + True + _Show event in systray + True + 0 + True + + + + False + False + + + + + True + True + _Disable showing event in systray + True + 0 + True + + + + False + False + 1 + + + + + 1 + + + + + True + 6 + + + True + True + _Show event in roster + True + 0 + True + + + + False + False + + + + + True + True + _Disable showing event in roster + True + 0 + True + + + + False + False + 1 + + + + + 2 + + + + + True + True + _Activate window manager's UrgencyHint to make chat window in taskbar flash + True + 0 + True + + + + False + False + 3 + + + + + + + True + Advanced Actions + + + label_item + + + + + 5 + + + + + 2 + + + + + True + 413 + + + True + True + True + gtk-close + True + 0 + + + + + + False + 3 + + + + + diff --git a/data/glade/chat_control_popup_menu.glade b/data/glade/chat_control_popup_menu.glade index b6a696355..c3ebb1e24 100644 --- a/data/glade/chat_control_popup_menu.glade +++ b/data/glade/chat_control_popup_menu.glade @@ -65,16 +65,6 @@ - - - True - _Compact View Alt+C - True - False - - - - True diff --git a/data/glade/features_window.glade b/data/glade/features_window.glade new file mode 100644 index 000000000..7f8db0b9d --- /dev/null +++ b/data/glade/features_window.glade @@ -0,0 +1,116 @@ + + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 12 + Features + 300 + 530 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 6 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + <b>List of possible features in Gajim:</b> + True + + + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_OUT + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + + + + + + 1 + + + + + True + 3 + 0 + GTK_SHADOW_NONE + + + True + 6 + 0 + 0.5 + 12 + + + True + 0 + True + + + + + + + True + <b>Description</b> + True + + + label_item + + + + + False + 3 + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + GTK_BUTTONBOX_END + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-close + True + 0 + + + + + + False + GTK_PACK_END + 2 + + + + + + diff --git a/data/glade/filetransfers.glade b/data/glade/filetransfers.glade index cc629da4e..3106b3663 100644 --- a/data/glade/filetransfers.glade +++ b/data/glade/filetransfers.glade @@ -1,392 +1,281 @@ - - - + + + - - - 12 - File Transfers - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - File Transfers - Shows a list of file transfers between you and others - - - - - - - True - False - 6 - - - - 460 - 150 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - False - False - False - False - - file transfers list - A list of active, completed and stopped file transfers - - - - - - - - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 6 - - - - True - False - Removes completed, cancelled and failed file transfers from the list - True - True - GTK_RELIEF_NORMAL - True - - Remove file transfer from the list. - This action removes single file transfer from the list. If the transfer is active, it is first stopped and then removed - - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-clear - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - Clean _up - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - False - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-media-pause - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Pause - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - False - Cancels the selected file transfer and removes incomplete file - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - Cancel file transfer - Cancels the selected file transfer - - - - - - - - True - Hides the window - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - GTK_PACK_END - - - - - - True - True - _Notify me when a file transfer is complete - True - GTK_RELIEF_NORMAL - True - False - False - True - - When a file transfer is complete show a popup notification - - - - - 0 - False - False - - - - - - - - - - - gtk-remove - True - - - - - - - _Continue - True - - - - - True - gtk-media-play - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - _Pause - True - - - - - True - gtk-media-pause - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - gtk-cancel - True - - - - - - - True - - - - - - _Open Containing Folder - True - - - - - True - gtk-directory - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - + + 12 + File Transfers + + File Transfers + Shows a list of file transfers between you and others + + + + + + True + 6 + + + 460 + 150 + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + False + + file transfers list + A list of active, completed and stopped file transfers + + + + + + + + + + + + + + True + True + _Notify me when a file transfer is complete + True + 0 + True + + When a file transfer is complete show a popup notification + + + + + False + False + 2 + + + + + True + 6 + GTK_BUTTONBOX_END + + + True + False + True + True + Removes completed, cancelled and failed file transfers from the list + 0 + + Remove file transfer from the list. + This action removes single file transfer from the list. If the transfer is active, it is first stopped and then removed + + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-clear + + + False + False + + + + + True + Clean _up + True + + + False + False + 1 + + + + + + + + + + + True + False + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-media-pause + + + False + False + + + + + True + _Pause + True + + + False + False + 1 + + + + + + + + + 1 + + + + + True + False + True + True + Cancels the selected file transfer and removes incomplete file + gtk-cancel + True + 0 + + Cancel file transfer + Cancels the selected file transfer + + + + + 2 + + + + + True + True + True + True + Hides the window + gtk-close + True + 0 + + + + 3 + + + + + False + GTK_PACK_END + 1 + + + + + + + + + gtk-remove + True + True + + + + + + True + _Continue + True + + + + True + gtk-media-play + 1 + + + + + + + _Pause + True + + + + True + gtk-media-pause + 1 + + + + + + + gtk-cancel + True + True + + + + + + True + + + + + _Open Containing Folder + True + + + + True + gtk-missing-image + 1 + + + + + diff --git a/data/glade/gajim_themes_window.glade b/data/glade/gajim_themes_window.glade index e64f4402c..db8d7d13e 100644 --- a/data/glade/gajim_themes_window.glade +++ b/data/glade/gajim_themes_window.glade @@ -408,7 +408,7 @@ Chat Banner - + True 9 2 diff --git a/data/glade/gc_control_popup_menu.glade b/data/glade/gc_control_popup_menu.glade index 1f5918e80..2c2e665e7 100644 --- a/data/glade/gc_control_popup_menu.glade +++ b/data/glade/gc_control_popup_menu.glade @@ -10,17 +10,17 @@ True - gtk-redo + gtk-edit 1 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Manage room + _Manage Room True @@ -60,7 +60,7 @@ True - _Destroy room + _Destroy Room True @@ -73,30 +73,25 @@ - - - - - _Compact View Alt+C - True - - - - - True - _Minimize - True - + True - gtk-goto-bottom + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-properties 1 - + + True + _Minimize on close + True + + + + True @@ -113,6 +108,11 @@ + + + True + + Click to see past conversation in this room diff --git a/data/glade/gc_occupants_menu.glade b/data/glade/gc_occupants_menu.glade index 15a90c35b..044a6bb4f 100644 --- a/data/glade/gc_occupants_menu.glade +++ b/data/glade/gc_occupants_menu.glade @@ -17,20 +17,6 @@ - - - True - _Add to Roster - True - - - True - gtk-add - 1 - - - - True @@ -101,6 +87,39 @@ + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Send _File + True + + + gtk-save + 1 + + + + + + + True + + + + + True + _Add to Roster + True + + + True + gtk-add + 1 + + + + True diff --git a/data/glade/history_manager.glade b/data/glade/history_manager.glade index 49225bf27..2d7802f95 100644 --- a/data/glade/history_manager.glade +++ b/data/glade/history_manager.glade @@ -1,392 +1,249 @@ - - - + + + - - - 6 - Gajim History Logs Manager - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 650 - 500 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - - - - - True - False - 6 - - - - True - True - 200 - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - True - False - False - False - - - - - - - True - False - - - - - - True - False - 0 - - - - True - <big><b>Welcome to Gajim History Logs Manager</b></big> + + 6 + Gajim History Logs Manager + 650 + 500 + + + + True + 6 + + + True + True + 200 + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + + + + + + + False + True + + + + + True + + + True + 0 + <big><b>Welcome to Gajim History Logs Manager</b></big> You can select logs from the left and/or search database from below. <b>WARNING:</b> If you plan to do massive deletions, please make sure Gajim is not running. Generally avoid deletions with contacts you currently chat with. - False - True - GTK_JUSTIFY_LEFT - True - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - False - False - False - False - - - - - - - 0 - True - True - - - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - True - False - False - False - - - - - - - 0 - True - True - - - - - True - True - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - True - True - True - 0 - - True - * - True - - - 0 - True - True - - - - - - True - True - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-find - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Search Database - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - 0 - False - False - - - - - 0 - False - True - - - - - - - - - - - True - Export - True - - - - - - - True - Delete - True - - - - True - gtk-remove - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - GTK_FILE_CHOOSER_ACTION_SAVE - True - False - False - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - True - - - - True - False - 24 - - - - True - GTK_BUTTONBOX_END - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - -6 - - - - - - True - True - True - True - gtk-save - True - GTK_RELIEF_NORMAL - True - -5 - - - - - 0 - False - True - GTK_PACK_END - - - - - - + True + True + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + False + + + + + + + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + True + + + + + + + 2 + + + + + True + True + + + + + + + True + 6 + + + True + True + * + True + + + + + True + True + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-find + + + False + False + + + + + True + _Search Database + True + + + False + False + 1 + + + + + + + + + False + False + 1 + + + + + False + 1 + + + + + + + + + True + Export + True + + + + + + True + Delete + True + + + True + gtk-remove + 1 + + + + + + + GDK_WINDOW_TYPE_HINT_DIALOG + GTK_FILE_CHOOSER_ACTION_SAVE + + + True + 24 + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + -6 + + + + + True + True + True + True + gtk-save + True + -5 + + + 1 + + + + + False + GTK_PACK_END + + + + + diff --git a/data/glade/manage_bookmarks_window.glade b/data/glade/manage_bookmarks_window.glade index 4dab0c5a6..26e168033 100644 --- a/data/glade/manage_bookmarks_window.glade +++ b/data/glade/manage_bookmarks_window.glade @@ -1,499 +1,323 @@ - - - + + + - - - 12 - Manage Bookmarks - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 550 - 300 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - True - False - 12 - - - - True - False - 12 - - - - True - False - 6 - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - False - False - False - True - False - False - False - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 6 - - - - True - True - True - gtk-add - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-remove - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - 0 - True - True - - - - - - True - 7 - 2 - False - 6 - 12 - - - - True - If checked, Gajim will join this group chat on startup - True - Auto join - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - 2 - 5 - 6 - fill - - - - - - - True - Password: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - - True - True - True - False - 0 - - True - * - False - - - 1 - 2 - 4 - 5 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 3 - 4 - - - - - - - True - Server: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - fill - - - - - - - True - Room: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - Nickname: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Title: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - Print status: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 6 - 7 - fill - - - - - - - True - - False - True - - - - 1 - 2 - 6 - 7 - fill - fill - - - - - 0 - False - True - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 12 - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-ok - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - - + + 12 + Manage Bookmarks + 550 + 300 + + + + True + 12 + + + True + 12 + + + True + 6 + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + False + + + + + + + True + 6 + GTK_BUTTONBOX_END + + + True + True + True + gtk-add + True + + + + + + True + True + True + gtk-remove + True + + + + 1 + + + + + False + 1 + + + + + + + True + 7 + 2 + 12 + 6 + + + True + 0 + Password: + + + 4 + 5 + GTK_FILL + + + + + + True + True + False + * + + + 1 + 2 + 4 + 5 + + + + + + True + True + * + + + 1 + 2 + 3 + 4 + + + + + + True + 0 + Server: + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + Room: + + + 2 + 3 + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + 2 + 3 + + + + + + True + True + * + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + Nickname: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Title: + + + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + + + + + + True + 0 + Print status: + + + 6 + 7 + GTK_FILL + + + + + + True + + + + + 1 + 2 + 6 + 7 + GTK_FILL + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + True + If checked, Gajim will join this group chat on startup + Auto join + True + True + + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Minimize on Auto Join + True + True + + + + 1 + + + + + 2 + 5 + 6 + + + + + False + 1 + + + + + + + True + 12 + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + + + + + + True + True + True + gtk-ok + True + + + + 1 + + + + + False + 1 + + + + + diff --git a/data/glade/message_window.glade b/data/glade/message_window.glade index ad23fad7b..77d9028b1 100644 --- a/data/glade/message_window.glade +++ b/data/glade/message_window.glade @@ -591,7 +591,7 @@ Status message 0.5 0 0 - PANGO_ELLIPSIZE_NONE + PANGO_ELLIPSIZE_END -1 False 0 diff --git a/data/glade/preferences_window.glade b/data/glade/preferences_window.glade index 82165ef7c..f82682c62 100644 --- a/data/glade/preferences_window.glade +++ b/data/glade/preferences_window.glade @@ -1,3384 +1,2166 @@ - - - + + + - - - 6 - Preferences - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - preferences - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 6 - - - - True - True - True - False - GTK_POS_TOP - False - False - - - - 12 - True - False - 6 - - - - True - Use t_rayicon (aka. notification area icon) - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - If checked, Gajim will remember the roster and chat window positions in the screen and the sizes of them next time you run it - True - Save _position and size for roster and chat windows - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - If checked, Gajim will display avatars of contacts in roster window and in group chats - True - Display a_vatars of contacts in roster - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - If checked, Gajim will display status messages of contacts under the contact name in roster window and in group chats - True - Display status _messages of contacts in roster - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - _Sort contacts by status - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 4 - 0 - 12 - 0 - - - - True - False - 12 - - - - True - False - 12 - - - - True - Default status _iconset: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - iconset_combobox - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - True - - - - 0 - False - True - - - - - - True - If checked, Gajim will use protocol-specific status icons. (eg. A contact from MSN will have the equivalent msn icon for status online, away, busy, etc...) - True - Use _transports iconsets - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - True - - - - - 0 - True - True - - - - - - True - False - 12 - - - - True - T_heme: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - theme_combobox - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - True - - - - 0 - False - True - - - - - - True - Configure color and font of the interface - True - Ma_nage... - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - - - - - - True - <b>Interface Customization</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 12 - False - True - - - - - False - True - - - - - - True - General - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - False - 6 - - - - True - False - 5 - - - - True - One message _window: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 1 - 0 - one_window_type_combobox - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - Never + + 6 + Preferences + preferences + + + + + True + 6 + + + True + True + False + + + True + 12 + 6 + + + True + Use t_rayicon (aka. notification area icon) + True + 0 + True + + + + False + False + + + + + True + True + If checked, Gajim will remember the roster and chat window positions in the screen and the sizes of them next time you run it + Save _position and size for roster and chat windows + True + 0 + True + + + + False + False + 1 + + + + + True + True + If checked, Gajim will display avatars of contacts in roster window and in group chats + Display a_vatars of contacts in roster + True + 0 + True + + + + False + False + 2 + + + + + True + True + If checked, Gajim will display status messages of contacts under the contact name in roster window and in group chats + Display status _messages of contacts in roster + True + 0 + True + + + + False + False + 3 + + + + + True + True + _Sort contacts by status + True + 0 + True + + + + False + False + 4 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 4 + 12 + + + True + 12 + + + True + 12 + + + True + Default status _iconset: + True + iconset_combobox + + + False + False + + + + + True + + + + False + 1 + + + + + True + True + If checked, Gajim will use protocol-specific status icons. (eg. A contact from MSN will have the equivalent msn icon for status online, away, busy, etc...) + Use _transports iconsets + True + 0 + True + + + + False + 2 + + + + + + + True + 12 + + + True + T_heme: + True + theme_combobox + + + False + False + + + + + True + + + + False + 1 + + + + + True + True + Configure color and font of the interface + Ma_nage... + True + 0 + + + + False + False + 2 + + + + + 1 + + + + + + + + + True + <b>Interface Customization</b> + True + + + label_item + + + + + False + 12 + 5 + + + + + False + + + + + True + General + + + tab + False + False + + + + + True + 12 + 6 + + + True + 5 + + + True + 1 + One message _window: + True + one_window_type_combobox + + + False + False + + + + + True + Never Always Per account Per type - False - True - - - - 0 - False - False - - - - - 0 - False - True - - - - - - True - _Highlight misspelled words - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - Some messages may include rich content (formatting, colors etc). If checked, Gajim will just display the raw message text. - True - Ignore rich content in incoming messages - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - False - 5 - - - - True - Treat all incoming messages as: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 1 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - Determined by sender + + + + False + False + 1 + + + + + False + + + + + True + Hides buttons in chatwindows to + _Make message windows compact + True + 0 + True + + + + False + False + 1 + + + + + True + True + Some messages may include rich content (formatting, colors etc). If checked, Gajim will just display the raw message text. + Ignore rich content in incoming messages + True + 0 + True + + + + False + False + 2 + + + + + True + _Highlight misspelled words + True + 0 + True + + + + False + False + 3 + + + + + True + 4 + + + True + 1 + Treat all incoming messages as: + + + False + False + + + + + True + Determined by sender Chat message Single message - False - True - - - - 0 - False - False - - - - - 0 - False - True - - - - - - True - False - 5 - - - - True - If not disabled, Gajim will replace ascii smilies like ':)' with equivalent animated or static graphical emoticons - True - False - - - - True - Emoticons: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 1 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 0 - False - False - - - - - - True - False - True - - - - 0 - False - True - - - - - 0 - False - True - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 12 - - - - True - False - 6 - - - - True - Print time: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - True - On every _message - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - Also known as iChat style - True - E_very 5 minutes - True - GTK_RELIEF_NORMAL - True - False - False - True - time_always_radiobutton - - - - 0 - False - False - - - - - - True - True - _Never - True - GTK_RELIEF_NORMAL - True - False - False - True - time_always_radiobutton - - - - 0 - False - False - - - - - 0 - False - True - - - - - - True - 4 - 4 - False - 6 - 12 - - - - True - _Incoming message: - True - False - GTK_JUSTIFY_CENTER - True - False - 0 - 0.5 - 0 - 0 - incoming_msg_colorbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - True - True - True - True - False - True - - - - 1 - 2 - 0 - 1 - - - - - - - - True - _Outgoing message: - True - False - GTK_JUSTIFY_CENTER - True - False - 0 - 0.5 - 0 - 0 - outgoing_msg_colorbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - True - False - True - - - - 3 - 4 - 0 - 1 - fill - - - - - - - True - _Status message: - True - False - GTK_JUSTIFY_CENTER - True - False - 0 - 0.5 - 0 - 0 - status_msg_colorbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - True - False - True - - - - 1 - 2 - 1 - 2 - - - - - - - - True - _URL: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - url_msg_colorbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - True - False - True - - - - 3 - 4 - 1 - 2 - fill - - - - - - - True - False - 0 - - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - False - - - - - - True - True - GTK_RELIEF_NORMAL - False - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-revert-to-saved - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Reset to Default Colors - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - 0 - False - False - - - - - 0 - 4 - 2 - 3 - fill - - - - - - True - False - 6 - - - - True - _Font: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - conversation_fontbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - True - True - True - False - False - True - - - - 0 - False - True - - - - - 0 - 2 - 3 - 4 - fill - - - - - - True - True - Use system _default - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 2 - 4 - 3 - 4 - fill - - - - - - 0 - False - True - - - - - - - - - - True - <b>Format of a line</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - False - True - - - - - - True - Chat - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - False - 12 - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - 2 - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - 3 - True - False - 6 - - - - True - Gajim will notify you for new events via a popup in the bottom right of the screen - True - _Notify me about it - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - Gajim will automatically show new events by popping up the relevant window - True - _Pop it up - True - GTK_RELIEF_NORMAL - True - False - False - True - notify_on_new_message_radiobutton - - - - 0 - False - False - - - - - - True - Gajim will only change the icon of the contact that triggered the new event - True - Show only in _roster - True - GTK_RELIEF_NORMAL - True - False - False - True - notify_on_new_message_radiobutton - - - - 0 - False - False - - - - - - - - - - True - When a new event (message, file transfer request etc..) is received, the following methods may be used to inform you about it. Please note that events about new messages only occur if it is a new message from a contact you are not already chatting with - True - False - - - - True - When new event is received - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - label_item - - - - - 0 - False - False - - - - - - True - False - 6 - - - - True - Notify me about contacts that: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - Gajim will notify you via a popup window in the bottom right of the screen about contacts that just signed in - True - Sign _in - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - Gajim will notify you via a popup window in the bottom right of the screen about contacts that just signed out - True - Sign _out - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - False - True - - - - - - True - True - Allow popup/notifications when I'm _away/na/busy/invisible - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - False - 0 - - - - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-open - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Advanced Notifications Control... - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - 0 - False - False - - - - - - - - - - - - - 0 - True - True - - - - - - - - - - True - <b>Visual Notifications</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - - True - Check this option, only if someone you don't have in the roster spams/annoys you. Use with caution, because it blocks all messages from any contact that is not in the roster - True - _Ignore events from contacts not in the roster - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - False - 12 - - - - True - Gajim can send and receive meta-information related to a conversation you may have with a contact. Here you can specify which chatstates you want to send to the other party. - True - False - - - - True - Outgoing Chat state noti_fications: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 0 - False - False - - - - - - True - All chat states + + + + False + False + 1 + + + + + False + 4 + + + + + True + 5 + + + True + If not disabled, Gajim will replace ascii smilies like ':)' with equivalent animated or static graphical emoticons + + + True + 1 + Emoticons: + + + + + False + False + + + + + True + + + + False + 1 + + + + + False + 5 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 12 + + + True + 6 + + + True + 0 + Print time: + + + False + False + + + + + True + True + On every _message + True + 0 + True + + + + False + False + 1 + + + + + True + True + Also known as iChat style + E_very 5 minutes + True + 0 + True + time_always_radiobutton + + + + False + False + 2 + + + + + True + True + _Never + True + 0 + True + time_always_radiobutton + + + + False + False + 3 + + + + + False + + + + + True + 4 + 4 + 12 + 6 + + + True + True + Use system _default + True + 0 + True + + + + 2 + 4 + 3 + 4 + GTK_FILL + + + + + + True + 6 + + + True + 0 + _Font: + True + conversation_fontbutton + + + + + True + True + 0 + + + + False + 1 + + + + + 2 + 3 + 4 + GTK_FILL + + + + + True + + + True + + + False + + + + + True + True + False + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-revert-to-saved + + + False + False + + + + + True + _Reset to Default Colors + True + + + False + False + 1 + + + + + + + + + False + False + 1 + + + + + 4 + 2 + 3 + GTK_FILL + + + + + True + True + 0 + + + + 3 + 4 + 1 + 2 + GTK_FILL + + + + + + True + 0 + _URL: + True + url_msg_colorbutton + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + True + 0 + + + + 1 + 2 + 1 + 2 + + + + + + + True + 0 + _Status message: + True + GTK_JUSTIFY_CENTER + True + status_msg_colorbutton + + + 1 + 2 + GTK_FILL + + + + + + True + True + 0 + + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Outgoing message: + True + GTK_JUSTIFY_CENTER + True + outgoing_msg_colorbutton + + + 2 + 3 + GTK_FILL + + + + + + True + True + True + True + True + 0 + + + + 1 + 2 + + + + + + + True + 0 + _Incoming message: + True + GTK_JUSTIFY_CENTER + True + incoming_msg_colorbutton + + + GTK_FILL + + + + + + False + 1 + + + + + + + + + True + <b>Format of a line</b> + True + + + label_item + + + + + False + False + 6 + + + + + 1 + False + + + + + True + Chat + + + tab + 1 + False + False + + + + + True + 12 + 12 + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + 2 + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 3 + 6 + + + True + True + Gajim will notify you for new events via a popup in the bottom right of the screen + _Notify me about it + True + 0 + True + + + + False + False + + + + + True + True + Gajim will automatically show new events by popping up the relevant window + _Pop it up + True + 0 + True + notify_on_new_message_radiobutton + + + + False + False + 1 + + + + + True + True + Gajim will only change the icon of the contact that triggered the new event + Show only in _roster + True + 0 + True + notify_on_new_message_radiobutton + + + + False + False + 2 + + + + + + + + + True + When a new event (message, file transfer request etc..) is received, the following methods may be used to inform you about it. Please note that events about new messages only occur if it is a new message from a contact you are not already chatting with + + + True + When new event is received + + + + + label_item + + + + + False + False + + + + + True + 6 + + + True + Notify me about contacts that: + + + False + False + + + + + True + True + Gajim will notify you via a popup window in the bottom right of the screen about contacts that just signed in + Sign _in + True + 0 + True + + + + False + False + 1 + + + + + True + True + Gajim will notify you via a popup window in the bottom right of the screen about contacts that just signed out + Sign _out + True + 0 + True + + + + False + False + 2 + + + + + False + 1 + + + + + True + True + Allow popup/notifications when I'm _away/na/busy/invisible + True + 0 + True + + + + False + False + 2 + + + + + True + True + + + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-open + + + False + False + + + + + True + _Advanced Notifications Control... + True + + + False + False + 1 + + + + + + + + + False + False + + + + + + + + + + + 3 + + + + + + + + + True + <b>Visual Notifications</b> + True + + + label_item + + + + + False + False + + + + + True + True + Check this option, only if someone you don't have in the roster spams/annoys you. Use with caution, because it blocks all messages from any contact that is not in the roster + _Ignore events from contacts not in the roster + True + 0 + True + + + + False + False + 1 + + + + + True + 12 + + + True + Gajim can send and receive meta-information related to a conversation you may have with a contact. Here you can specify which chatstates you want to send to the other party. + + + True + Outgoing Chat state noti_fications: + True + + + + + False + False + + + + + True + All chat states Composing only Disabled - False - True - - - - 0 - False - True - - - - - 0 - False - False - - - - - - True - False - 12 - - - - True - Gajim can send and receive meta-information related to a conversation you may have with a contact. Here you can specify which chatstates you want to display in chat windows. - True - False - - - - True - Displayed Chat state noti_fications: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 0 - False - False - - - - - - True - All chat states + + + + False + 1 + + + + + False + False + 2 + + + + + True + 12 + + + True + Gajim can send and receive meta-information related to a conversation you may have with a contact. Here you can specify which chatstates you want to display in chat windows. + + + True + Displayed Chat state noti_fications: + True + + + + + False + False + + + + + True + All chat states Composing only Disabled - False - True - - - - 0 - False - True - - - - - 0 - False - False - - - - - - True - True - False - 0 - - - - True - False - 6 - - - - True - True - 6 - - - - True - True - Play _sounds - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - True - True - - - - - - False - 5 - - - - True - _Player: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - soundplayer_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - True - True - True - 0 - - True - * - False - - - - 0 - True - True - - - - - 0 - True - True - - - - - 0 - False - False - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - True - False - False - False - - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - True - True - True - 0 - - True - * - False - - - - 0 - True - True - - - - - - True - True - ... - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - True - True - GTK_RELIEF_NORMAL - True - - - - - True - gtk-media-play - 4 - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - False - - - - - 0 - False - True - - - - - - - - True - <b>Sounds</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - False - True - - - - - - True - Events - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - False - 12 - - - - True - 2 - 4 - False - 6 - 12 - - - - True - True - Auto _away after: - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - True - Auto _not available after: - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - minutes - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - minutes - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - 1 - 0.5 - 0 - 1 - 0 - 0 - 0 - 0 - - - - 50 - True - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 12 1 720 1 10 10 - - - - - - 1 - 2 - 0 - 1 - fill - fill - - - - - - True - 1 - 0.5 - 0 - 1 - 0 - 0 - 0 - 0 - - - - 50 - True - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 20 1 1440 1 10 10 - - - - - - 1 - 2 - 1 - 2 - fill - fill - - - - - - True - The auto away status message - True - True - True - 0 - - True - * - False - - - - 3 - 4 - 0 - 1 - - - - - - - True - The auto not available status message - True - True - True - 0 - - True - * - False - - - - 3 - 4 - 1 - 2 - - - - - - 0 - False - True - - - - - - True - False - 6 - - - - True - Ask status message when I: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - 14 - - - - True - True - Sign _in - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Sign _out - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - False - False - - - - - 0 - False - False - - - - - - True - Works for Rhythmbox and Muine players. For more players, please visit http://trac.gajim.org/wiki/GajimAndMusicPlayer - True - Set status message to reflect currently playing _music track - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - An example: If you have enabled status message for away, Gajim won't ask you anymore for a status message when you change your status to away; it will use the default one set here - True - False - - - - True - True - False - 0 - - - - - 6 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - True - False - False - False - - - - - - - - True - Default Status Messages - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - - - 0 - False - True - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - False - 6 - - - - 5 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - False - False - False - True - False - False - False - - - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_START - 0 - - - - 5 - True - True - True - gtk-new - True - GTK_RELIEF_NORMAL - True - - - - - - - 5 - True - True - True - gtk-delete - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - 0 - True - True - - - - - - 5 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_NONE - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - 0 - False - False - - - - - - - - - - True - <b>Preset Status Messages</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - False - True - - - - - - True - Status - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - False - 6 - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - Autodetect on every Gajim startup + + + + False + 1 + + + + + False + False + 3 + + + + + True + True + + + True + 6 + + + True + 6 + True + + + True + True + Play _sounds + True + 0 + True + + + + + + 5 + + + True + _Player: + True + soundplayer_entry + + + + + True + True + + + + 1 + + + + + 1 + + + + + False + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + + + + + + 1 + + + + + True + 6 + + + True + True + + + + + + True + True + ... + True + 0 + + + + False + False + 1 + + + + + True + True + 0 + + + + True + gtk-media-play + + + + + False + False + 2 + + + + + False + 2 + + + + + + + True + <b>Sounds</b> + True + + + label_item + + + + + 4 + + + + + 2 + False + + + + + True + Events + + + tab + 2 + False + False + + + + + True + 12 + 12 + + + True + 2 + 4 + 12 + 6 + + + True + True + The auto not available status message + + + + 3 + 4 + 1 + 2 + + + + + + True + True + The auto away status message + + + + 3 + 4 + + + + + + True + 1 + 0 + + + 50 + True + True + 20 1 1440 1 10 10 + 1 + + + + + + 1 + 2 + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 1 + 0 + + + 50 + True + True + 12 1 720 1 10 10 + 1 + + + + + + 1 + 2 + GTK_FILL + GTK_FILL + + + + + True + 0 + minutes + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + minutes + + + 2 + 3 + GTK_FILL + + + + + + True + True + Auto _not available after: + True + 0 + True + + + + 1 + 2 + GTK_FILL + + + + + + True + True + Auto _away after: + True + 0 + True + + + + GTK_FILL + + + + + + False + + + + + True + 6 + + + True + Ask status message when I: + + + False + False + + + + + True + 14 + + + True + True + Sign _in + True + 0 + True + + + + False + False + + + + + True + True + Sign _out + True + 0 + True + + + + False + False + 1 + + + + + False + False + 1 + + + + + False + False + 1 + + + + + True + True + Works for Rhythmbox and Muine players. For more players, please visit http://trac.gajim.org/wiki/GajimAndMusicPlayer + Set status message to reflect currently playing _music track + True + 0 + True + + + + False + False + 2 + + + + + True + An example: If you have enabled status message for away, Gajim won't ask you anymore for a status message when you change your status to away; it will use the default one set here + + + True + True + + + + True + True + 6 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + + + + + + + True + Default Status Messages + + + label_item + + + + + + + False + 3 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + 6 + + + True + True + 5 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + False + + + + + + + + + True + GTK_BUTTONBOX_START + + + True + True + True + 5 + gtk-new + True + 0 + + + + + + True + True + True + 5 + gtk-delete + True + 0 + + + + 1 + + + + + False + 1 + + + + + + + True + True + 5 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + + + + + False + False + 1 + + + + + + + + + True + <b>Preset Status Messages</b> + True + + + label_item + + + + + 4 + + + + + 3 + False + + + + + True + Status + + + tab + 3 + False + False + + + + + True + 12 + 6 + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + Autodetect on every Gajim startup Always use GNOME default applications Always use KDE default applications Always use XFCE4 default applications Custom - False - True - - - - 0 - True - True - - - - - - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - 3 - 2 - False - 6 - 12 - - - - True - _Browser: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - custom_browser_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - _Mail client: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - custom_mail_client_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - - 1 - 2 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - - 1 - 2 - 0 - 1 - - - - - - - True - _File manager: - True - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - custom_file_manager_entry - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - - 1 - 2 - 2 - 3 - - - - - - - - - - - True - <b>Custom</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - - - - - - True - <b>Applications</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - True - Notify on new _GMail email - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - If checked, Gajim will also include information about the sender of the new emails - True - Display _extra email details - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - - - - - True - <b>GMail Options</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 1 - 0 - 0 - 12 - 0 - - - - True - False - 6 - - - - True - True - _Log status changes of contacts - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Allow _OS information to be sent - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Always check to see if Gajim is the _default Jabber client on startup - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - - - - - True - <b>Miscellaneous</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - - True - 0 - 0.5 - GTK_SHADOW_NONE - - - - 6 - True - 0.5 - 0.5 - 1 - 0 - 0 - 0 - 12 - 335 - - - - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-open - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Open... - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - - - True - <b>Advanced Configuration Editor</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - False - False - - - - - False - True - - - - - - True - Advanced - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 15 - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - - + + + + + + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 3 + 2 + 12 + 6 + + + True + True + + + + 1 + 2 + 2 + 3 + + + + + + True + 0 + _File manager: + True + custom_file_manager_entry + + + 2 + 3 + GTK_FILL + + + + + + True + True + + + + 1 + 2 + + + + + + True + True + + + + 1 + 2 + 1 + 2 + + + + + + True + 0 + _Mail client: + True + custom_mail_client_entry + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + _Browser: + True + custom_browser_entry + + + GTK_FILL + + + + + + + + + + True + <b>Custom</b> + True + + + label_item + + + + + 1 + + + + + + + + + True + <b>Applications</b> + True + + + label_item + + + + + False + False + + + + + True + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + True + Notify on new _GMail email + True + 0 + True + + + + False + False + + + + + True + True + If checked, Gajim will also include information about the sender of the new emails + Display _extra email details + True + 0 + True + + + + False + False + 1 + + + + + + + + + True + <b>GMail Options</b> + True + + + label_item + + + + + False + False + 1 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 12 + + + True + 6 + + + True + True + _Log status changes of contacts + True + 0 + True + + + + False + False + + + + + True + True + Allow _OS information to be sent + True + 0 + True + + + + False + False + 1 + + + + + True + True + Always check to see if Gajim is the _default Jabber client on startup + True + 0 + True + + + + False + False + 2 + + + + + + + + + True + <b>Miscellaneous</b> + True + + + label_item + + + + + False + False + 2 + + + + + True + 0 + GTK_SHADOW_NONE + + + True + 6 + 0 + 12 + 335 + + + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-open + + + False + False + + + + + True + _Open... + True + + + False + False + 1 + + + + + + + + + + + + + True + <b>Advanced Configuration Editor</b> + True + + + label_item + + + + + False + False + 3 + + + + + 4 + False + + + + + True + Advanced + + + tab + 4 + False + False + + + + + + + True + 15 + GTK_BUTTONBOX_END + + + True + True + True + gtk-close + True + 0 + + + + + + False + 1 + + + + + diff --git a/data/glade/privacy_list_window.glade b/data/glade/privacy_list_window.glade index 5e857a72c..d2d527c76 100644 --- a/data/glade/privacy_list_window.glade +++ b/data/glade/privacy_list_window.glade @@ -1,757 +1,526 @@ - - - + + + - - - 6 - True - Privacy List - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - 600 - True - False - 0 - - - - True - True - 0 - - - - True - <i>Privacy List</i> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - True - Active for this session - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - - True - True - Active on each startup - True - GTK_RELIEF_NORMAL - True - False - False - True - - - - 0 - False - False - - - - - 0 - False - True - - - - - - True - - - 5 - False - False - - - - - - True - <b>List of rules</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 5 - False - False - - - - - - True - - False - True - - - - 5 - False - True - - - - - - True - True - 0 - - - - 5 - True - True - gtk-add - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - 5 - True - True - gtk-remove - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - 6 - True - True - gtk-edit - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 0 - False - True - - - - - - 5 - False - 0 - - - - True - - - 5 - True - True - - - - - - True - <b>Add / Edit a rule</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 5 - False - False - - - - - - True - False - 0 - - - - True - True - 0 - - - - True - True - Allow - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - True - Deny - True - GTK_RELIEF_NORMAL - True - False - False - True - edit_allow_radiobutton - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - True - 0 - - - - 5 - True - False - 0 - - - - True - True - JabberID - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 5 - False - False - - - - - - True - True - True - True - 0 - - True - - False - - - 5 - True - True - - - - - 0 - True - True - - - - - - 5 - True - False - 0 - - - - True - True - all in the group - True - GTK_RELIEF_NORMAL - True - False - False - True - edit_type_jabberid_radiobutton - - - 5 - False - False - - - - - - True - - False - True - - - 5 - True - True - - - - - 0 - True - True - - - - - - 5 - True - False - 0 - - - - True - True - all by subscription - True - GTK_RELIEF_NORMAL - True - False - False - True - edit_type_jabberid_radiobutton - - - 5 - False - False - - - - - - True - none + + True + 6 + Privacy List + + + + 600 + True + + + True + True + + + True + <i>Privacy List</i> + True + + + False + False + + + + + True + True + Active for this session + True + 0 + True + + + + False + False + 1 + + + + + True + True + Active on each startup + True + 0 + True + + + + False + False + 2 + + + + + False + + + + + True + + + False + False + 5 + 1 + + + + + True + <b>List of rules</b> + True + + + False + False + 5 + 2 + + + + + True + + + + + False + 5 + 3 + + + + + True + True + + + True + True + 5 + gtk-add + True + 0 + + + + False + False + + + + + True + True + 5 + gtk-remove + True + 0 + + + + False + False + 1 + + + + + True + True + 6 + gtk-edit + True + 0 + + + + False + False + 2 + + + + + False + 4 + + + + + True + 5 + + + True + + + 5 + + + + + True + <b>Add / Edit a rule</b> + True + + + False + False + 5 + 1 + + + + + True + + + True + True + + + True + True + Allow + True + 0 + True + + + False + False + + + + + True + True + Deny + True + 0 + True + edit_allow_radiobutton + + + False + False + 1 + + + + + + + True + True + + + True + 5 + + + True + True + JabberID + True + 0 + True + + + False + False + 5 + + + + + True + True + + + 5 + 1 + + + + + + + True + 5 + + + True + True + all in the group + True + 0 + True + edit_type_jabberid_radiobutton + + + False + False + 5 + + + + + True + + + + 5 + 1 + + + + + 1 + + + + + True + 5 + + + True + True + all by subscription + True + 0 + True + edit_type_jabberid_radiobutton + + + False + False + 5 + + + + + True + none both from to - False - True - - - 5 - True - True - - - - - 0 - True - True - - - - - - 10 - True - False - 0 - - - - True - True - All - True - GTK_RELIEF_NORMAL - True - False - False - True - edit_type_jabberid_radiobutton - - - 0 - False - False - - - - - 0 - False - False - - - - - 0 - True - True - - - - - - True - True - 0 - - - - True - True - to send me messages - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - True - to send me queries - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - True - to view my status - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - - True - True - to send me status - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - True - 0 - - - - True - False - 0 - - - - True - Order: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 5 - False - False - - - - - - True - True - 1 - 0 - False - GTK_UPDATE_ALWAYS - False - False - 1 0 100 1 10 10 - - - 0 - False - True - - - - - 0 - True - True - - - - - - 5 - True - True - gtk-save - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - - - 0 - False - True - - - - - - 6 - True - GTK_BUTTONBOX_END - 0 - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - True - True - - - - - - + + + 5 + 1 + + + + + 2 + + + + + True + 10 + + + True + True + All + True + 0 + True + edit_type_jabberid_radiobutton + + + False + False + + + + + False + False + 3 + + + + + 1 + + + + + True + True + + + True + True + to send me messages + True + 0 + True + + + False + False + + + + + True + True + to send me queries + True + 0 + True + + + False + False + 1 + + + + + True + True + to view my status + True + 0 + True + + + False + False + 2 + + + + + True + True + to send me status + True + 0 + True + + + False + False + 3 + + + + + 2 + + + + + 2 + + + + + True + True + + + True + + + True + Order: + + + False + False + 5 + + + + + True + True + 1 0 100 1 10 10 + 1 + + + False + 1 + + + + + + + True + True + 5 + gtk-save + True + 0 + + + + False + False + 1 + + + + + 3 + + + + + 5 + + + + + True + + + False + 6 + + + + + True + 6 + GTK_BUTTONBOX_END + + + True + True + True + gtk-close + True + 0 + + + + + + 7 + + + + + diff --git a/data/glade/profile_window.glade b/data/glade/profile_window.glade index b9c4394ab..6f033ba58 100644 --- a/data/glade/profile_window.glade +++ b/data/glade/profile_window.glade @@ -1,1943 +1,1156 @@ - - - + + + - - - Personal Information - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 12 - - - - 6 - True - True - True - GTK_POS_TOP - False - False - - - - 12 - True - 7 - 4 - False - 6 - 12 - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 4 - 4 - 5 - - - - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 6 - 12 - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 2 - 3 - - - - - - - True - Family: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Middle: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Prefix: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Given: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Suffix: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - More - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 0 - 1 - - - - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 5 - 5 - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 2 - 3 - - - - - - - True - Street: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - City: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - State: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Extra Address: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Postal Code: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - Country: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - Address - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 5 - 6 - - - - - - - True - Homepage: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 4 - 3 - 4 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 5 - 6 - - - - - - - True - Name: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Nickname: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Phone No.: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 5 - 6 - fill - - - - - - - True - Format: YYYY-MM-DD - True - False - - - - True - Birthday: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 2 - 3 - 5 - 6 - fill - - - - - - - True - E-Mail: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - - True - Avatar: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 6 - 7 - fill - - - - - - - True - False - 0 - - - - True - GTK_RELIEF_NORMAL - True - - - - - True - False - False - - - - True - 0.5 - 0 - 0 - 0 - - - - - - - 0 - False - False - - - - - - True - True - Click to set your avatar - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 1 - 4 - 6 - 7 - - expand - - - - - False - True - - - - - - True - Personal Info - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - 5 - 4 - False - 6 - 12 - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 4 - 3 - 4 - - - - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 5 - 5 - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 1 - 2 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 3 - 4 - 2 - 3 - - - - - - - True - Street: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - City: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - State: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Extra Address: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Postal Code: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - Country: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - Address - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 2 - 3 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 4 - 5 - - - - - - - True - Company: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Position: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Department: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Role: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - E-Mail: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - fill - - - - - - - True - Phone No.: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - False - True - - - - - - True - Work - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 6 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - 70 - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - False - True - - - - - - True - About - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - 0 - True - True - - - - - - 6 - True - False - 0 - - - - True - GTK_PROGRESS_LEFT_TO_RIGHT - 0 - 0.10000000149 - PANGO_ELLIPSIZE_NONE - - - 0 - False - False - - - - - - True - GTK_BUTTONBOX_END - 12 - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-ok - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - True - True - - - - - 0 - False - True - - - - - - True - False - - - 0 - False - False - - - - - - + + Personal Information + + + + + True + 12 + + + True + 6 + + + True + 12 + 7 + 4 + 12 + 6 + + + True + + + True + 0 + + + + True + False + + + True + 0 + gtk-missing-image + + + + + + + False + False + + + + + True + True + True + Click to set your avatar + True + 0 + + + + False + False + 1 + + + + + 1 + 4 + 6 + 7 + + GTK_EXPAND + + + + + True + 0 + Avatar: + + + 6 + 7 + GTK_FILL + + + + + + True + 0 + E-Mail: + + + 4 + 5 + GTK_FILL + + + + + + True + Format: YYYY-MM-DD + + + True + 0 + 0 + Birthday: + + + + + 2 + 3 + 5 + 6 + GTK_FILL + + + + + + True + 0 + 0 + Phone No.: + + + 5 + 6 + GTK_FILL + + + + + + True + 0 + 0 + Nickname: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Name: + + + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + 5 + 6 + + + + + + True + True + * + + + 1 + 4 + 3 + 4 + + + + + + True + 0 + 0 + Homepage: + + + 3 + 4 + + + + + + + True + True + * + + + 3 + 4 + 5 + 6 + + + + + + True + True + + + True + 6 + 3 + 4 + 5 + 5 + + + True + 0 + 0 + Country: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Postal Code: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Extra Address: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + State: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + City: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Street: + + + GTK_FILL + + + + + + True + True + * + + + 3 + 4 + 2 + 3 + + + + + + True + True + * + + + 1 + 2 + 2 + 3 + + + + + + True + True + * + + + 3 + 4 + 1 + 2 + + + + + + True + True + * + + + 1 + 2 + 1 + 2 + + + + + + True + True + * + + + 3 + 4 + + + + + + True + True + * + + + 1 + 2 + + + + + + + + True + 0 + 0 + Address + + + label_item + + + + + 4 + 2 + 3 + + + + + + True + True + * + + + 3 + 4 + + + + + + True + True + + + True + 6 + 3 + 4 + 12 + 6 + + + + + + + + + True + 0 + 0 + Suffix: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Given: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Prefix: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Middle: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Family: + + + GTK_FILL + + + + + + True + True + * + + + 3 + 4 + 2 + 3 + + + + + + True + True + * + + + 1 + 2 + 2 + 3 + + + + + + True + True + * + + + 1 + 2 + 1 + 2 + + + + + + True + True + * + + + 3 + 4 + + + + + + True + True + * + + + 1 + 2 + + + + + + + + True + 0 + 0 + Full Name + + + label_item + + + + + 4 + 1 + 2 + + + + + + True + True + * + + + 1 + 4 + 4 + 5 + + + + + + True + True + * + + + 1 + 2 + + + + + + False + + + + + True + 0 + 0 + Personal Info + + + tab + False + False + + + + + True + 12 + 5 + 4 + 12 + 6 + + + + + + + + + True + 0 + 0 + Phone No.: + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + 0 + E-Mail: + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + 0 + Role: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Department: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Position: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Company: + + + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + 4 + 5 + + + + + + True + True + + + True + 6 + 3 + 4 + 5 + 5 + + + True + 0 + 0 + Country: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Postal Code: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Extra Address: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + State: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + City: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Street: + + + GTK_FILL + + + + + + True + True + * + + + 3 + 4 + 2 + 3 + + + + + + True + True + * + + + 1 + 2 + 2 + 3 + + + + + + True + True + * + + + 3 + 4 + 1 + 2 + + + + + + True + True + * + + + 1 + 2 + 1 + 2 + + + + + + True + True + * + + + 3 + 4 + + + + + + True + True + * + + + 1 + 2 + + + + + + + + True + 0 + 0 + Address + + + label_item + + + + + 4 + 2 + 3 + + + + + + True + True + * + + + 1 + 4 + 3 + 4 + + + + + + True + True + * + + + 3 + 4 + 1 + 2 + + + + + + True + True + * + + + 1 + 2 + 1 + 2 + + + + + + True + True + * + + + 3 + 4 + + + + + + True + True + * + + + 1 + 2 + + + + + + 1 + False + + + + + True + 0 + 0 + Work + + + tab + 1 + False + False + + + + + True + True + 6 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + 70 + True + True + GTK_WRAP_WORD + + + + + 2 + False + + + + + True + 0 + 0 + About + + + tab + 2 + False + False + + + + + + + True + 6 + + + True + 0.10000000149 + + + False + False + + + + + True + 12 + GTK_BUTTONBOX_END + + + True + True + True + gtk-cancel + True + 0 + + + + + + True + True + True + gtk-ok + True + 0 + + + + 1 + + + + + 1 + + + + + False + 1 + + + + + True + False + + + False + False + 2 + + + + + diff --git a/data/glade/roster_contact_context_menu.glade b/data/glade/roster_contact_context_menu.glade index 4352365ef..0d35d74a6 100644 --- a/data/glade/roster_contact_context_menu.glade +++ b/data/glade/roster_contact_context_menu.glade @@ -65,9 +65,9 @@ - + True - Send cus_tom status + Send Cus_tom Status True @@ -95,7 +95,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - _Manage contact + _Manage Contact True @@ -123,6 +123,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-edit + 1 @@ -142,7 +143,7 @@ - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set Custom _Avatar @@ -152,6 +153,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-orientation-portrait + 1 @@ -159,6 +161,7 @@ True + True Add Special _Notification True @@ -244,7 +247,7 @@ True - gtk-yes + gtk-stop 1 @@ -298,6 +301,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-properties + 1 @@ -309,7 +313,7 @@ - gtk-dialog-info + gtk-info True True diff --git a/data/glade/roster_window.glade b/data/glade/roster_window.glade index 16fb5bbb4..1f1f10fb5 100644 --- a/data/glade/roster_window.glade +++ b/data/glade/roster_window.glade @@ -266,12 +266,40 @@ - + True Frequently Asked Questions (online) _FAQ True + + + True + gtk-dialog-question + 1 + + + + + + + True + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Fea_tures + True + + + + True + gtk-properties + 1 + + diff --git a/data/glade/search_window.glade b/data/glade/search_window.glade index 6a3e6133a..efa3dd993 100644 --- a/data/glade/search_window.glade +++ b/data/glade/search_window.glade @@ -1,191 +1,215 @@ - - - + + + - - - 12 - Search - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 6 - - - - True - False - 0 - - - - True - Please wait while retrieving search form... - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - False - - - - - - True - GTK_PROGRESS_LEFT_TO_RIGHT - 0 - 0.10000000149 - PANGO_ELLIPSIZE_NONE - - - 0 - True - False - - - - - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 6 - - - - True - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-find - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Search - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - False - True - - - - - - + + 12 + Search + + + + + True + 6 + + + True + + + True + Please wait while retrieving search form... + + + False + + + + + True + 0.10000000149 + + + False + 1 + + + + + + + + + + True + 6 + GTK_BUTTONBOX_END + + + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-add + + + False + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Add contact + True + + + False + False + 1 + + + + + + + + + + + False + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + 0 + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-info + + + False + False + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + _Information + True + + + False + False + 1 + + + + + + + + + 1 + + + + + True + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-find + + + False + False + + + + + True + _Search + True + + + False + False + 1 + + + + + + + + + 2 + + + + + True + True + True + gtk-close + True + 0 + + + + 3 + + + + + False + 1 + + + + + diff --git a/data/glade/service_discovery_window.glade b/data/glade/service_discovery_window.glade index e5b471c7e..197b18a9a 100644 --- a/data/glade/service_discovery_window.glade +++ b/data/glade/service_discovery_window.glade @@ -1,421 +1,269 @@ - - - + + + - - - 6 - - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 450 - 420 - True - False - Service Discovery - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - True - False - 6 - - - + + False + False + + + + + True + 3 + 3 + 6 + + + + + + + + + True + _Address: + True + address_comboboxentry + + + 3 + GTK_FILL + + + + + + True + True + True + True + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-jump-to + + + False + False + + + + + True + G_o + True + + + False + False + 1 + + + + + + + + + 2 + 3 + 3 + GTK_FILL + + + + + + True + + + + + + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + False + 1 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_ETCHED_IN + + + True + True + False + + + + + + 2 + + + + + True + True + 6 + + + True + _Filter: + True + filter_entry + + + False + False + + + + + True + True + * + + + + 1 + + + + + False + False + 3 + + + + + True + 12 + + + True + True + 0.10000000149 + + + False + False + + + + + True + + + 1 + + + + + True + 6 + + + True + True + True + True + gtk-close + True + 0 + + + + False + False + GTK_PACK_END + + + + + False + 2 + + + + + False + 2 + 4 + + + + + diff --git a/data/glade/single_message_window.glade b/data/glade/single_message_window.glade index 955bc9bc1..6c3f06641 100644 --- a/data/glade/single_message_window.glade +++ b/data/glade/single_message_window.glade @@ -1,548 +1,346 @@ - - - + + + - - - 6 - - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 550 - 280 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - - - - - - True - False - 6 - - - - True - 3 - 3 - False - 6 - 12 - - - - True - Subject: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 3 - 0 - 1 - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 2 - 3 - - - - - - - True - 0 - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 3 - 1 - 2 - - - - - - - True - From: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - To: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - 0 - False - True - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - - - - 0 - True - True - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - 0 - True - True - - - - - - True - GTK_BUTTONBOX_END - 12 - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - - - True - Send message - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-jump-to - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - Sen_d - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - Reply to this message - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-ok - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Reply - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - - True - Send message and close window - True - True - GTK_RELIEF_NORMAL - True - - - - - True - 0.5 - 0.5 - 0 - 0 - 0 - 0 - 0 - 0 - - - - True - False - 2 - - - - True - gtk-ok - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - False - - - - - - True - _Send & Close - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - - - - - - 6 - False - True - - - - - - + + 6 + 550 + 280 + + + + + True + 6 + + + True + 3 + 3 + 12 + 6 + + + True + True + 0 + To: + + + 1 + 2 + GTK_FILL + + + + + + True + True + 0 + From: + + + GTK_FILL + + + + + + True + True + True + * + + + 1 + 3 + 1 + 2 + + + + + + True + 0 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + True + * + + + 1 + 2 + 2 + 3 + + + + + + True + True + True + * + + + 1 + 3 + + + + + + True + Subject: + + + 2 + 3 + GTK_FILL + + + + + + False + + + + + True + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + + + + 1 + + + + + True + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + True + True + GTK_WRAP_WORD + + + + + 2 + + + + + True + 12 + GTK_BUTTONBOX_END + + + True + True + True + True + gtk-close + True + 0 + + + + + + True + True + True + True + gtk-cancel + True + 0 + + + + 1 + + + + + True + True + True + True + Send message + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-jump-to + + + False + False + + + + + True + Sen_d + True + + + False + False + 1 + + + + + + + + + 2 + + + + + True + True + True + True + Reply to this message + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + _Reply + True + + + False + False + 1 + + + + + + + + + 3 + + + + + True + True + True + True + Send message and close window + 0 + + + + True + 0 + 0 + + + True + 2 + + + True + gtk-ok + + + False + False + + + + + True + _Send & Close + True + + + False + False + 1 + + + + + + + + + 4 + + + + + False + 6 + 3 + + + + + diff --git a/data/glade/vcard_information_window.glade b/data/glade/vcard_information_window.glade index ad55ab284..17685e322 100644 --- a/data/glade/vcard_information_window.glade +++ b/data/glade/vcard_information_window.glade @@ -1,2768 +1,1597 @@ - - - + + + - - - 12 - Contact Information - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - - - True - False - 12 - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - True - True - GTK_POS_TOP - False - False - - - - 12 - True - False - 6 - - - - True - False - 12 - - - - True - 5 - 2 - False - 6 - 12 - - - - True - Jabber ID: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Resource: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Status: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Client: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - fill - - - - - - - True - OS: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - False - False - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 1 - 2 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 3 - 4 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - True - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 4 - 5 - - - - - - - True - True - False - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 1 - 2 - 2 - 3 - fill - fill - - - - - 0 - True - True - - - - - - True - False - 6 - - - - True - User avatar: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - None - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - False - False - - - - - True - 0.5 - 0 - 0 - 0 - - - - - 0 - False - False - - - - - - True - Configured avatar: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - True - - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - True - True - Click to force avatar - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - True - False - 0 - - - - 6 - True - 1 - 4 - False - 6 - 12 - - - - True - False - False - - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 1 - 2 - 0 - 1 - - - - - - - True - False - False - - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 3 - 4 - 0 - 1 - - - - - - - True - Subscription: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Ask: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - - - True - More - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - True - True - - - - - False - True - - - - - - True - Contact - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - 6 - 4 - False - 6 - 12 - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 6 - 12 - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 2 - 3 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 2 - 3 - - - - - - - True - Family: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Middle: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Prefix: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Given: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Suffix: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - More - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 1 - 2 - - - - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 5 - 5 - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 2 - 3 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 2 - 3 - - - - - - - True - Street: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - City: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - State: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Extra Address: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Postal Code: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - Country: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - Address - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 2 - 3 - - - - - - - True - Homepage: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - - - - - - - - True - Name: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Nickname: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Phone No.: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 5 - 6 - fill - - - - - - - True - Format: YYYY-MM-DD - True - False - - - - True - Birthday: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - - - 2 - 3 - 5 - 6 - fill - - - - - - - True - E-Mail: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 4 - 4 - 5 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 5 - 6 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 5 - 6 - - - - - - False - True - - - - - - True - Personal Info - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 12 - True - 5 - 4 - False - 6 - 12 - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 4 - 3 - 4 - - - - - - - True - True - False - 0 - - - - 6 - True - 3 - 4 - False - 5 - 5 - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 0 - 1 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 1 - 2 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 2 - 3 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 3 - 4 - 2 - 3 - - - - - - - True - Street: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - City: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - State: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 2 - 3 - fill - - - - - - - True - Extra Address: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Postal Code: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - Country: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 2 - 3 - fill - - - - - - - - - True - Address - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - label_item - - - - - 0 - 4 - 2 - 3 - - - - - - - True - True - - False - False - GTK_JUSTIFY_LEFT - False - True - 0 - 0 - 5 - 5 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 1 - 2 - 4 - 5 - - - - - - - True - Company: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 0 - 1 - fill - - - - - - - True - Position: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 1 - 2 - fill - - - - - - - True - Department: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 0 - 1 - fill - - - - - - - True - Role: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 2 - 3 - 1 - 2 - fill - - - - - - - True - E-Mail: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 3 - 4 - fill - - - - - - - True - Phone No.: - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - 1 - 4 - 5 - fill - - - - - - False - True - - - - - - True - Work - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 6 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - 70 - True - True - False - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - False - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - False - True - - - - - - True - About - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - 6 - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - 70 - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_NONE - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - - False - True - - - - - - True - Comments - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - 0 - True - True - - - - - - True - False - 0 - - - - True - GTK_PROGRESS_LEFT_TO_RIGHT - 0 - 0.10000000149 - PANGO_ELLIPSIZE_NONE - - - 0 - False - False - - - - - - True - GTK_BUTTONBOX_END - 0 - - - - True - True - True - gtk-close - True - GTK_RELIEF_NORMAL - True - - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - + + 12 + Contact Information + + + + + True + 12 + + + True + True + 0 + True + + + False + False + + + + + True + + + True + 12 + 6 + + + True + 12 + + + True + 5 + 2 + 12 + 6 + + + True + + + True + True + 0 + 0 + 5 + 5 + True + + + + + 1 + 2 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + True + 0 + 0 + 5 + 5 + True + True + + + 1 + 2 + 4 + 5 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 3 + 4 + + + + + + True + False + + + True + True + 0 + 0 + 5 + 5 + True + + + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + True + 0 + 0 + OS: + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + 0 + Client: + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + 0 + Status: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Resource: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Jabber ID: + + + GTK_FILL + + + + + + + + True + 6 + + + True + User avatar: + + + False + False + + + + + True + None + + + False + False + 1 + + + + + True + False + + + + True + 0 + gtk-missing-image + + + + + False + False + 2 + + + + + True + Configured avatar: + + + False + False + 3 + + + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + gtk-missing-image + + + False + False + 4 + + + + + 1 + + + + + + + True + True + + + True + 6 + 1 + 4 + 12 + 6 + + + True + 0 + 0 + Ask: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Subscription: + + + GTK_FILL + + + + + + True + False + + + True + 0 + 0 + 5 + 5 + + + + + 3 + 4 + + + + + + True + False + + + True + 0 + 0 + 5 + 5 + + + + + 1 + 2 + + + + + + + + True + 0 + 0 + More + + + label_item + + + + + 1 + + + + + False + + + + + True + 0 + 0 + Contact + + + tab + False + False + + + + + True + 12 + 6 + 4 + 12 + 6 + + + + + + + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 5 + 6 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 5 + 6 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 4 + 4 + 5 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + + + + + + True + 0 + E-Mail: + + + 4 + 5 + GTK_FILL + + + + + + True + Format: YYYY-MM-DD + + + True + 0 + 0 + Birthday: + + + + + 2 + 3 + 5 + 6 + GTK_FILL + + + + + + True + 0 + 0 + Phone No.: + + + 5 + 6 + GTK_FILL + + + + + + True + 0 + 0 + Nickname: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Name: + + + GTK_FILL + + + + + + True + 0 + 0 + Homepage: + + + 3 + 4 + + + + + + + True + True + + + True + 6 + 3 + 4 + 5 + 5 + + + True + 0 + 0 + Country: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Postal Code: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Extra Address: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + State: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + City: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Street: + + + GTK_FILL + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + + + True + 0 + 0 + Address + + + label_item + + + + + 4 + 2 + 3 + + + + + + True + True + + + True + 6 + 3 + 4 + 12 + 6 + + + + + + + + + True + 0 + 0 + Suffix: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Given: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Prefix: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Middle: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Family: + + + GTK_FILL + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + + + True + 0 + 0 + Full Name + + + label_item + + + + + 4 + 1 + 2 + + + + + + 1 + False + + + + + True + 0 + 0 + Personal Info + + + tab + 1 + False + False + + + + + True + 12 + 5 + 4 + 12 + 6 + + + + + + + + + True + 0 + 0 + Phone No.: + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + 0 + E-Mail: + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + 0 + Role: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Department: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Position: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Company: + + + GTK_FILL + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 4 + 5 + + + + + + True + True + + + True + 6 + 3 + 4 + 5 + 5 + + + True + 0 + 0 + Country: + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + Postal Code: + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Extra Address: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + State: + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + 0 + City: + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 0 + Street: + + + GTK_FILL + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + + + True + 0 + 0 + Address + + + label_item + + + + + 4 + 2 + 3 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 4 + 3 + 4 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + 1 + 2 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 3 + 4 + + + + + + True + True + 0 + 0 + 5 + 5 + True + + + 1 + 2 + + + + + + 2 + False + + + + + True + 0 + 0 + Work + + + tab + 2 + False + False + + + + + True + True + 6 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + 70 + True + True + False + GTK_WRAP_WORD + False + + + + + 3 + False + + + + + True + 0 + 0 + About + + + tab + 3 + False + False + + + + + True + True + 6 + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + + + 70 + True + True + GTK_WRAP_WORD + + + + + 4 + False + + + + + True + Comments + + + tab + 4 + False + False + + + + + 1 + + + + + True + + + True + 0.10000000149 + + + False + False + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-close + True + 0 + + + + + + 1 + + + + + 2 + + + + + diff --git a/data/glade/xml_console_window.glade b/data/glade/xml_console_window.glade index 9491b9586..548275c61 100644 --- a/data/glade/xml_console_window.glade +++ b/data/glade/xml_console_window.glade @@ -181,8 +181,8 @@ True True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC + GTK_POLICY_NEVER + GTK_POLICY_NEVER GTK_SHADOW_IN GTK_CORNER_TOP_LEFT diff --git a/data/glade/zeroconf_contact_context_menu.glade b/data/glade/zeroconf_contact_context_menu.glade index edbb0f064..4ad215baa 100644 --- a/data/glade/zeroconf_contact_context_menu.glade +++ b/data/glade/zeroconf_contact_context_menu.glade @@ -1,153 +1,113 @@ - - - + + + - - - - - - True - Start _Chat - True - - - - True - gtk-jump-to - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - _Rename - True - - - - True - gtk-refresh - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - Edit _Groups - True - - - - - - True - - - - - - True - Send _File - True - - - - True - gtk-file - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - Assign Open_PGP Key - True - - - - - True - gtk-dialog-authentication - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - Add Special _Notification - True - - - - True - gtk-info - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - - - True - - - - - - gtk-info - True - - - - - - _History - True - - - - True - gtk-justify-fill - 1 - 0.5 - 0.5 - 0 - 0 - - - - - - + + + + True + Start _Chat + True + + + True + gtk-jump-to + 1 + + + + + + + _Rename + True + + + True + gtk-refresh + 1 + + + + + + + Edit _Groups + True + + + + + True + + + + + True + Send _File + True + + + True + gtk-missing-image + 1 + + + + + + + Assign Open_PGP Key + True + + + + True + gtk-missing-image + 1 + + + + + + + True + True + Add Special _Notification + True + + + True + gtk-info + 1 + + + + + + + True + + + + + gtk-info + True + True + + + + + _History + True + + + True + gtk-justify-fill + 1 + + + + + diff --git a/data/iconsets/crystal/16x16/message.gif b/data/iconsets/crystal/16x16/event.gif similarity index 100% rename from data/iconsets/crystal/16x16/message.gif rename to data/iconsets/crystal/16x16/event.gif diff --git a/data/iconsets/dcraven/16x16/message.png b/data/iconsets/dcraven/16x16/event.png similarity index 100% rename from data/iconsets/dcraven/16x16/message.png rename to data/iconsets/dcraven/16x16/event.png diff --git a/data/iconsets/dcraven/32x32/message.png b/data/iconsets/dcraven/32x32/event.png similarity index 100% rename from data/iconsets/dcraven/32x32/message.png rename to data/iconsets/dcraven/32x32/event.png diff --git a/data/iconsets/gnome/16x16/message.gif b/data/iconsets/gnome/16x16/event.gif similarity index 100% rename from data/iconsets/gnome/16x16/message.gif rename to data/iconsets/gnome/16x16/event.gif diff --git a/data/iconsets/goojim/16x16/message.png b/data/iconsets/goojim/16x16/event.png similarity index 100% rename from data/iconsets/goojim/16x16/message.png rename to data/iconsets/goojim/16x16/event.png diff --git a/data/iconsets/goojim/32x32/message.png b/data/iconsets/goojim/32x32/event.png similarity index 100% rename from data/iconsets/goojim/32x32/message.png rename to data/iconsets/goojim/32x32/event.png diff --git a/data/iconsets/gossip/16x16/message.png b/data/iconsets/gossip/16x16/event.png similarity index 100% rename from data/iconsets/gossip/16x16/message.png rename to data/iconsets/gossip/16x16/event.png diff --git a/data/iconsets/gota/16x16/message.gif b/data/iconsets/gota/16x16/event.gif similarity index 100% rename from data/iconsets/gota/16x16/message.gif rename to data/iconsets/gota/16x16/event.gif diff --git a/data/iconsets/gota/32x32/message.gif b/data/iconsets/gota/32x32/event.gif similarity index 100% rename from data/iconsets/gota/32x32/message.gif rename to data/iconsets/gota/32x32/event.gif diff --git a/data/iconsets/jabberbulb/16x16/message.gif b/data/iconsets/jabberbulb/16x16/event.gif similarity index 100% rename from data/iconsets/jabberbulb/16x16/message.gif rename to data/iconsets/jabberbulb/16x16/event.gif diff --git a/data/iconsets/nuvola/16x16/message.gif b/data/iconsets/nuvola/16x16/event.gif similarity index 100% rename from data/iconsets/nuvola/16x16/message.gif rename to data/iconsets/nuvola/16x16/event.gif diff --git a/data/iconsets/simplebulb/16x16/message.png b/data/iconsets/simplebulb/16x16/event.png similarity index 100% rename from data/iconsets/simplebulb/16x16/message.png rename to data/iconsets/simplebulb/16x16/event.png diff --git a/data/iconsets/stellar/16x16/message.gif b/data/iconsets/stellar/16x16/event.gif similarity index 100% rename from data/iconsets/stellar/16x16/message.gif rename to data/iconsets/stellar/16x16/event.gif diff --git a/data/iconsets/sun/16x16/message.gif b/data/iconsets/sun/16x16/event.gif similarity index 100% rename from data/iconsets/sun/16x16/message.gif rename to data/iconsets/sun/16x16/event.gif diff --git a/data/other/cacerts.pem b/data/other/cacerts.pem new file mode 100644 index 000000000..26a90f150 --- /dev/null +++ b/data/other/cacerts.pem @@ -0,0 +1,2474 @@ +ABAecom_=sub.__Am._Bankers_Assn.=_Root_CA +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEF +BQAwgYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2Fz +aGluZ3RvbjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFC +QS5FQ09NIFJvb3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3Ry +dXN0LmNvbTAeFw05OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQsw +CQYDVQQGEwJVUzELMAkGA1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24x +FzAVBgNVBAoTDkFCQS5FQ09NLCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBS +b290IENBMSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBkaWdzaWd0cnVzdC5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx0xHgeVVDBwhMywVC +AOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM0KLMsFWWU4RmBQDaREmA2FQK +pSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFGPR7wuSw0X4x8TAgpnUBV +6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGULOR4SCQaJRk665Wc +OQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZsiSrK2jMTecJV +jO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU+/94Qby9 +cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYBAf8C +AQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0k +qS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvT +ZOirvRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHeg +TYjHInYZw8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm +/lowdiT/QHI8eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgx +fexgeqMiKL0ZJGA/O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJ +TzFxiNmIf1Q= +-----END CERTIFICATE----- + +AddTrust_External_Root +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4 +dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h +bCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzEL +MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1B +ZGRUcnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1 +c3QgRXh0ZXJuYWwgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALf3GjPm8gAELTngTlvtH7xsD821+iO2zt6bETOXpClMfZOfvUq8 +k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfwTz/oMp50 +ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504 +B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDez +eWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5 +aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0WicCAwEAAaOB +3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0PBAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6 +xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdv +cmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJ +KoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5R +xNKWt9x+Tu5w/Rw56wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjT +K3rMUUKhemPR5ruhxSvCNr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1 +n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHx +REzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49O +hgQ= +-----END CERTIFICATE----- + +AddTrust_Low-Value_Services_Root +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU +UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw +HhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU +UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwze +xODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY654eyNAbFvAWlA3yCyykQruGI +gb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWroulpOj0O +M3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1Lc +sRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5 +mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG +9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0OBBYEFJWxtPCU +tr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk +ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAx +IENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0 +MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9 +tTEv2dB8Xfjea4MYeDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL +/bscVjby/rK25Xa71SJlpz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlV +g3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6 +tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust_Public_Services_Root +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU +UCBOZXR3b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAe +Fw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNF +MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+ +A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c ++OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1id9NEHif2 +P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKX +C1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8R +s3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9 +BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQWBBSBPjfYkrAf +d59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCB +jgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkG +A1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU +cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmu +G7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbj +PGsye/Kf8Lb93/AoGEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bY +GozH7ZxOmuASu7VqTITh4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6 +NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9HEufOX1362Kqx +My3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust_Qualified_Certificates_Root +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU +UCBOZXR3b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9v +dDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYT +AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3Qg +VFRQIE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoek +n0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKk +IhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3KP0q6p6z +sLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1t +UvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R ++Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvES +a0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0GA1UdDgQWBBQ5 +lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkw +ZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL +ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVh +bGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2Vh +lRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx9 +5dr6h+sNNVJn0J6XdgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKF +Yqa0p9m9N5xotS1WfbC3P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVA +wRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQw +dOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +America_Online_Root_Certification_Authority_1 +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV +UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l +cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4X +DTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp +Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCa +xlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CGv2BlnEtUiMJIxUo5vxTjWVXl +GbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44zDyL9Hy7n +BzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145Lcx +VR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiE +mf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCu +JKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Zo/Z5 +9m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUA +A4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF +Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOM +IOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTI +dGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j +8uB9Gr784N/Xx6dssPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America_Online_Root_Certification_Authority_2 +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV +UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l +cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4X +DTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp +Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssN +t79Hc9PwVU3dxgz6sWYFas14tNwC206B89enfHG8dWOgXeMHDEjsJcQDIPT/ +DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8f3SkWq7x +uhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE +18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxr +kJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMD +bi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8BPeraunzgWGcX +uVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn6KVu +Y8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9 +W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ +o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48 +ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124Hhn +AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNee +MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypL +M7PmG2tZTiLMubekJcmnxPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qf +tIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjR +Ywu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R ++FXgJXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr ++CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVM +nNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMADjMSW7yV5TKQqLPGbIOt +d+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh1NolNscI +WC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZ +ZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y +3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz +2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw +RY8mkaKO/qk= +-----END CERTIFICATE----- + +AOL_Time_Warner_Root_Certification_Authority_1 +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC +VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB +bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAw +MFoXDTM3MTEyMDE1MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB +T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg +SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U0pPlLYnKhHw/EEMbjIt8hFj4JHxI +zyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItITuLCxFlpMGK2MKKMCxGZYTVt +fu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAfRC+iYkGzuxgh28pxPIzs +trkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqFzQ6axOAAsNUl6twr +5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqhBC4aMqiaILGc +LCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEAAaNjMGEw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jYPXy+ +XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/ +BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNM +eUWn9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7 +CegCgTXTCt8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77Bf +WgDrvq2g+EQFZ7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oT +LW4jYYehY0KswsuXn2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCz +vhGbRWeDhhmH05i9CBoWH1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmw +X7A5KGgOc90lmt4S +-----END CERTIFICATE----- + +AOL_Time_Warner_Root_Certification_Authority_2 +-----BEGIN CERTIFICATE----- +MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC +VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB +bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw +MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB +T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg +SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs +RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs +i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg +/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ +2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0 +ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X ++Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml +J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh +EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo +Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ +Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex +MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA +FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0 +cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF +ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY +vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/ +drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la +5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks +mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5 +O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD +062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41 +xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H +hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL +Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg= +-----END CERTIFICATE----- + +Baltimore_CyberTrust_Root +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG +EwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0 +MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUx +MjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNV +BAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZ +QmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+h +Xe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gR +QKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP +wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1 +pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNT +Px8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC +AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1Ud +EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA +A4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkT +I7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/ +oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67 +G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +beTRUSTed_Root_CA-Baltimore_Implementation +-----BEGIN CERTIFICATE----- +MIIFajCCBFKgAwIBAgIEPLU9RjANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK +EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG +A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EtQmFsdGltb3JlIEltcGxlbWVudGF0 +aW9uMB4XDTAyMDQxMTA3Mzg1MVoXDTIyMDQxMTA3Mzg1MVowZjESMBAGA1UE +ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx +BgNVBAMTKmJlVFJVU1RlZCBSb290IENBLUJhbHRpbW9yZSBJbXBsZW1lbnRh +dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALx+xDmcjOPW +HIb/ymKt4H8wRXqOGrO4x/nRNv8i805qX4QQ+2aBw5R5MdKR4XeOGCrDFN5R +9U+jK7wYFuK13XneIviCfsuBH/0nLI/6l2Qijvj/YaOcGx6Sj8CoCd8JEey3 +fTGaGuqDIQY8n7pc/5TqarjDa1U0Tz0yH92BFODEPM2dMPgwqZfT7syj0B9f +HBOB1BirlNFjw55/NZKeX0Tq7PQiXLfoPX2k+YmpkbIq2eszh+6l/ePazIjm +iSZuxyuC0F6dWdsU7JGDBcNeDsYq0ATdcT0gTlgn/FP7eHgZFLL8kFKJOGJg +B7Sg7KxrUNb9uShr71ItOrL/8QFArDcCAwEAAaOCAh4wggIaMA8GA1UdEwEB +/wQFMAMBAf8wggG1BgNVHSAEggGsMIIBqDCCAaQGDysGAQQBsT4AAAEJKIOR +MTCCAY8wggFIBggrBgEFBQcCAjCCAToaggE2UmVsaWFuY2Ugb24gb3IgdXNl +IG9mIHRoaXMgQ2VydGlmaWNhdGUgY3JlYXRlcyBhbiBhY2tub3dsZWRnbWVu +dCBhbmQgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5k +YXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgdGhlIENlcnRpZmlj +YXRpb24gUHJhY3RpY2UgU3RhdGVtZW50IGFuZCB0aGUgUmVseWluZyBQYXJ0 +eSBBZ3JlZW1lbnQsIHdoaWNoIGNhbiBiZSBmb3VuZCBhdCB0aGUgYmVUUlVT +VGVkIHdlYiBzaXRlLCBodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20vcHJvZHVj +dHNfc2VydmljZXMvaW5kZXguaHRtbDBBBggrBgEFBQcCARY1aHR0cDovL3d3 +dy5iZXRydXN0ZWQuY29tL3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWww +HQYDVR0OBBYEFEU9w6nR3D8kVpgccxiIav+DR+22MB8GA1UdIwQYMBaAFEU9 +w6nR3D8kVpgccxiIav+DR+22MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B +AQUFAAOCAQEASZK8o+6svfoNyYt5hhwjdrCAWXf82n+0S9/DZEtqTg6t8n1Z +dwWtColzsPq8y9yNAIiPpqCy6qxSJ7+hSHyXEHu67RMdmgduyzFiEuhjA6p9 +beP4G3YheBufS0OM00mG9htc9i5gFdPp43t1P9ACg9AYgkHNZTfqjjJ+vWuZ +XTARyNtIVBw74acT02pIk/c9jH8F6M7ziCpjBLjqflh8AXtb4cV97yHgjQ5d +UX2xZ/2jvTg2xvI4hocalmhgRvsoFEdV4aeADGvi6t9NfJBIoDa9CReJf8Py +05yc493EG931t3GzUwWJBtDLSoDByFOQtTwxiBdQn8nEDovYqAJjDQ== +-----END CERTIFICATE----- + +beTRUSTed_Root_CA +-----BEGIN CERTIFICATE----- +MIIFLDCCBBSgAwIBAgIEOU99hzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG +EwJXVzESMBAGA1UEChMJYmVUUlVTVGVkMRswGQYDVQQDExJiZVRSVVNUZWQg +Um9vdCBDQXMxGjAYBgNVBAMTEWJlVFJVU1RlZCBSb290IENBMB4XDTAwMDYy +MDE0MjEwNFoXDTEwMDYyMDEzMjEwNFowWjELMAkGA1UEBhMCV1cxEjAQBgNV +BAoTCWJlVFJVU1RlZDEbMBkGA1UEAxMSYmVUUlVTVGVkIFJvb3QgQ0FzMRow +GAYDVQQDExFiZVRSVVNUZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBANS0c3oTCjhVAb6JVuGUntS+WutKNHUbYSnE4a0IYCF4 +SP+00PpeQY1hRIfo7clY+vyTmt9P6j41ffgzeubx181vSUs9Ty1uDoM6GHh3 +o8/n9E1z2Jo7Gh2+lVPPIJfCzz4kUmwMjmVZxXH/YgmPqsWPzGCgc0rXOD8V +cr+il7dw6K/ifhYGTPWqZCZyByWtNfwYsSbX2P8ZDoMbjNx4RWc0PfSvHI3k +bWvtILNnmrRhyxdviTX/507AMhLn7uzf/5cwdO2NR47rtMNE5qdMf1ZD6Li8 +tr76g5fmu/vEtpO+GRg+jIG5c4gW9JZDnGdzF5DYCW5jrEq2I8QBoa2k5MUC +AwEAAaOCAfgwggH0MA8GA1UdEwEB/wQFMAMBAf8wggFZBgNVHSAEggFQMIIB +TDCCAUgGCisGAQQBsT4BAAAwggE4MIIBAQYIKwYBBQUHAgIwgfQagfFSZWxp +YW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz +IGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0 +ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGFuZCBjZXJ0aWZpY2F0aW9u +IHByYWN0aWNlIHN0YXRlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGJl +VFJVU1RlZCdzIHdlYiBzaXRlLCBodHRwczovL3d3dy5iZVRSVVNUZWQuY29t +L3ZhdWx0L3Rlcm1zMDEGCCsGAQUFBwIBFiVodHRwczovL3d3dy5iZVRSVVNU +ZWQuY29tL3ZhdWx0L3Rlcm1zMDQGA1UdHwQtMCswKaAnoCWkIzAhMRIwEAYD +VQQKEwliZVRSVVNUZWQxCzAJBgNVBAYTAldXMB0GA1UdDgQWBBQquZtpLjub +2M3eKjEENGvKBxirZzAfBgNVHSMEGDAWgBQquZtpLjub2M3eKjEENGvKBxir +ZzAOBgNVHQ8BAf8EBAMCAf4wDQYJKoZIhvcNAQEFBQADggEBAHlh26Nebhax +6nZR+csVm8tpvuaBa58oH2U+3RGFktToQb9+M70j5/Egv6S0phkBxoyNNXxl +pE8JpNbYIxUFE6dDea/bow6be3ga8wSGWsb2jCBHOElQBp1yZzrwmAOtlmdE +/D8QDYZN5AA7KXvOOzuZhmElQITcE2K3+spZ1gMe1lMBzW1MaFVA4e5rxyoA +AEiCswoBw2AqDPeCNe5IhpbkdNQ96gFxugR1QKepfzk5mlWXKWWuGVUlBXJH +0+gY3Ljpr0NzARJ0o+FcXxVdJPP55PS2Z2cS52QiivalQaYctmBjRYoQtLpG +EK5BV2VsPyMQPyEQWbfkQN0mDCP2qq4= +-----END CERTIFICATE----- + +beTRUSTed_Root_CA_-_Entrust_Implementation +-----BEGIN CERTIFICATE----- +MIIGUTCCBTmgAwIBAgIEPLVPQDANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK +EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG +A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EgLSBFbnRydXN0IEltcGxlbWVudGF0 +aW9uMB4XDTAyMDQxMTA4MjQyN1oXDTIyMDQxMTA4NTQyN1owZjESMBAGA1UE +ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx +BgNVBAMTKmJlVFJVU1RlZCBSb290IENBIC0gRW50cnVzdCBJbXBsZW1lbnRh +dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0RAOqEmq1 +Q+xVkrYwfTVXDNvzDSduTPdQqJtOK2/b9a0cS12zqcH+e0TrW6MFDR/FNCsw +ACnxeECypP869AGIF37m1CbTukzqMvtDd5eHI8XbQ6P1KqNRXuE70mVpflUV +m3rnafdE4Fe1FehmYA8NA/uCjqPoEXtsvsdjDheT389Lrm5zdeDzqrmkwAkb +hepxKYhBMvnwKg5sCfJ0a2ZsUhMfGLzUPvfYbiCeyv78IZTuEyhL11xeDGbu +6bsPwTSxfwh28z0mcMmLJR1iJAzqHHVOwBLkuhMdMCktVjMFu5dZfsZJT4nX +LySotohAtWSSU1Yk5KKghbNekLQSM80CAwEAAaOCAwUwggMBMIIBtwYDVR0g +BIIBrjCCAaowggGmBg8rBgEEAbE+AAACCSiDkTEwggGRMIIBSQYIKwYBBQUH +AgIwggE7GoIBN1JlbGlhbmNlIG9uIG9yIHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNyZWF0ZXMgYW4gYWNrbm93bGVkZ21lbnQgYW5kIGFjY2VwdGFuY2Ug +b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29u +ZGl0aW9ucyBvZiB1c2UsIHRoZSBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0 +YXRlbWVudCBhbmQgdGhlIFJlbHlpbmcgUGFydHkgQWdyZWVtZW50LCB3aGlj +aCBjYW4gYmUgZm91bmQgYXQgdGhlIGJlVFJVU1RlZCB3ZWIgc2l0ZSwgaHR0 +cHM6Ly93d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRl +eC5odG1sMEIGCCsGAQUFBwIBFjZodHRwczovL3d3dy5iZXRydXN0ZWQuY29t +L3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWwwEQYJYIZIAYb4QgEBBAQD +AgAHMIGJBgNVHR8EgYEwfzB9oHugeaR3MHUxEjAQBgNVBAoTCWJlVFJVU1Rl +ZDEbMBkGA1UECxMSYmVUUlVTVGVkIFJvb3QgQ0FzMTMwMQYDVQQDEypiZVRS +VVNUZWQgUm9vdCBDQSAtIEVudHJ1c3QgSW1wbGVtZW50YXRpb24xDTALBgNV +BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMjA0MTEwODI0MjdagQ8yMDIyMDQx +MTA4NTQyN1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFH1w5a44iwY/qhwa +j/nPJDCqhIQWMB0GA1UdDgQWBBR9cOWuOIsGP6ocGo/5zyQwqoSEFjAMBgNV +HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkq +hkiG9w0BAQUFAAOCAQEAKrgXzh8QlOu4mre5X+za95IkrNySO8cgjfKZ5V04 +ocI07cUTWVwFtStPYZuR+0H8/NU8TZh2BvWBfevdkObRVlTa4y0MnxEylCIB +evZsLHRnBMylj44ss0O1lKLQfelifwa+JwGDnjr9iu6YQ0pr17WXOzq/T220 +Y/ozADQuLW2WyXvKmWO6vvT2MKAtmJbpVkQFqUSjYRDrgqFnXbxdJ3Wqiig2 +KjiS2d2kXgClzMx8KSreKJCrt+G2/30lC0DYqjSjLd4H61/OCt3Kfjp9JsFi +aDrmLzfzgYYhxKlkqu9FNtEaZnz46TfW1mG+oq1I59/mdP7TbX3SJdysYlep +9w== +-----END CERTIFICATE----- + +beTRUSTed_Root_CA_-_RSA_Implementation +-----BEGIN CERTIFICATE----- +MIIFaDCCBFCgAwIBAgIQO1nHe81bV569N1KsdrSqGjANBgkqhkiG9w0BAQUF +ADBiMRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBS +b290IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1w +bGVtZW50YXRpb24wHhcNMDIwNDExMTExODEzWhcNMjIwNDEyMTEwNzI1WjBi +MRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290 +IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVt +ZW50YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkujQw +CY5X0LkGLG9uJIAiv11DpvpPrILnHGhwhRujbrWqeNluB0s/6d/16uhUoWGK +Di9pdRi3DOUUjXFumLhV/AyV0Jtu4S2I1DpAa5LxmZZk3tv/ePTulh1HiXzU +vrmIdyM6CeYEnm2qXtLIvZpOGd+J6lsOfsPktPDgaTuID0GQ+NRxQyTBjyZL +O1bp/4xsN+lFrYWMU8NghpBKlsmzVLC7F/AcRdnUGxlkVgoZ98zh/4avflhe +rHqQH8koOUV7orbHnB/ahdQhhlkwk75TMzf270HPM8ercmsl9fNTGwxMLvF1 +S++gh/f+ihXQbNXL+WhTuXAVE8L1LvtDNXUtAgMBAAGjggIYMIICFDAMBgNV +HRMEBTADAQH/MIIBtQYDVR0gBIIBrDCCAagwggGkBg8rBgEEAbE+AAADCSiD +kTEwggGPMEEGCCsGAQUFBwIBFjVodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20v +cHJvZHVjdHNfc2VydmljZXMvaW5kZXguaHRtbDCCAUgGCCsGAQUFBwICMIIB +OhqCATZSZWxpYW5jZSBvbiBvciB1c2Ugb2YgdGhpcyBDZXJ0aWZpY2F0ZSBj +cmVhdGVzIGFuIGFja25vd2xlZGdtZW50IGFuZCBhY2NlcHRhbmNlIG9mIHRo +ZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlv +bnMgb2YgdXNlLCB0aGUgQ2VydGlmaWNhdGlvbiBQcmFjdGljZSBTdGF0ZW1l +bnQgYW5kIHRoZSBSZWx5aW5nIFBhcnR5IEFncmVlbWVudCwgd2hpY2ggY2Fu +IGJlIGZvdW5kIGF0IHRoZSBiZVRSVVNUZWQgd2ViIHNpdGUsIGh0dHA6Ly93 +d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1s +MAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSp7BR++dlDzFMrFK3P9/BZiUHN +GTAdBgNVHQ4EFgQUqewUfvnZQ8xTKxStz/fwWYlBzRkwDQYJKoZIhvcNAQEF +BQADggEBANuXsHXqDMTBmMpWBcCorSZIry0g6IHHtt9DwSwddUvUQo3neqh0 +3GZCWYez9Wlt2ames30cMcH1VOJZJEnl7r05pmuKmET7m9cqg5c0Lcd9NUwt +NLg+DcTsiCevnpL9UGGCqGAHFFPMZRPB9kdEadIxyKbdLrML3kqNWz2rDcI1 +UqJWN8wyiyiFQpyRQHpwKzg21eFzGh/l+n5f3NacOzDq28BbJ1zTcwfBwvNM +m2+fG8oeqqg4MwlYsq78B+g23FW6L09A/nq9BqaBwZMifIYRCgZ3SK41ty8y +mmFei74pnykkiFY5LKjSq5YDWtRIn7lAhAuYaPsBQ9Yb4gmxlxw= +-----END CERTIFICATE----- + +CAcert_org_-_root_CA +-----BEGIN CERTIFICATE----- +MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO +BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi +MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ +ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ +8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 +zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y +fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 +w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc +G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k +epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q +laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ +QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU +fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 +YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w +ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY +gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe +MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 +IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy +dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw +czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 +dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl +aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC +AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg +b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB +ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc +nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg +18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c +gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl +Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY +sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T +SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF +CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum +GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk +zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW +omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD +-----END CERTIFICATE----- + + + + +Certum_Root_CA +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYT +AlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNl +cnR1bSBDQTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJ +BgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNV +BAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AM6xwS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYV +M42sLQnFdvkrOYCJ5JdLkKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/Ox +LjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE +7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/KUz/iDsaW +VhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu +/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESS +bLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQaTOs9qmdvLdTN +/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvgGrZg +FCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqT +E5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYV +IZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo_AAA_Services_root +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJH +QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm +b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFB +IENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIz +MTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENB +IExpbWl0ZWQxITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5AnfRu4ep2hxxNRUSO +vkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhGC1Pqy0wk +wLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfH +dr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf04 +9vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULi +mAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cmez6KJcfA3Z3m +NWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEKIz6W +8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwu +Y29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG +9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHC +v8S5dIa2LX1rzNLzRt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdV +CYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAV +GI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C12yxow+ev+to +51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo_Secure_Services_root +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJH +QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm +b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2Vj +dXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4 +MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIg +TWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2Rv +IENBIExpbWl0ZWQxJDAiBgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2 +aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMBxM4KK0HDr +c4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP9nQ95IDC ++DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8 +j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWC +iIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtG +Cd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz6YiO/O1R65Nx +Tq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4EFgQU +PNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNo +dHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiF +Gv45jN5bBAS0VPmjZ55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXG +De+X3EyrEeFryzHRbPtIgKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsF +Vy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfU +a7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6s +Cx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo_Trusted_Services_root +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJH +QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm +b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1 +c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0y +ODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVy +IE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9k +byBDQSBMaW1pdGVkMSUwIwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNl +cnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhT +WvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh73TkVvFVI +xO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9 +C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/ +oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW1O24zG71++IsWL1/ +T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7kUlcsutT6vif +R4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1UdDgQW +BBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2 +hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2Vy +dmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdT +mw7pSqBYaWcOrp32pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+C +l5EfKNsYEYwq5GWDVxISjBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/g +hhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVF +wL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOj +GM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +Digital_Signature_Trust_Co._Global_CA_1 +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG +EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw +DwYDVQQLEwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQw +MjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy +ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEB +AQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlR +EmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth +zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0 +dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E +YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg +U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNV +BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIx +MDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5fpFpRhgTCgJ3 +pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAMBgNV +HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3 +DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN +QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomA +sH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6 +w4pl +-----END CERTIFICATE----- + +Digital_Signature_Trust_Co._Global_CA_2 +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGp +MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM +YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv +LjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDEx +ODE4NTVaFw0wODExMjgxODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE +CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp +Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDEx +FjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRp +Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdfWvnTLnUv2Chi0ZMv/E3U +q4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uKxBmd9LIO/BZsubEF +koPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBEzUNKcI5YhZXh +TizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F5X5yP4Wd +lGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMvOnNn +7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+ +LegzZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvV +WlHG4VMElo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX +8ngvYzZAOONGDx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn8 +6Oawde3uPclwx12qgUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsT +F7ANUkz+/m9c4pFuHf2kYtdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A== +-----END CERTIFICATE----- + +Digital_Signature_Trust_Co._Global_CA_3 +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG +EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw +DwYDVQQLEwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3 +MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy +ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEB +AQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fB +w18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e +ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd +55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E +YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg +U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNV +BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIw +OTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6CTShlgDzJQW6s +NS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAMBgNV +HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3 +DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR +xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLb +dHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlih +w6ID +-----END CERTIFICATE----- + +Digital_Signature_Trust_Co._Global_CA_4 +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGp +MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM +YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv +LjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAy +MjQ2MTZaFw0wODExMjcyMjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE +CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp +Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIx +FjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRp +Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbVp9oaBBg5kkp4o4HC9Xd6 +ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWwBZoPFflrWXJW8vo5 +/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl5WJp3OXuAFK9 +MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi3sOP17ih +YqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+QVCv +bK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWog +WxyQ2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6 +HE3K1GjNI3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV +6YyDfFk/xPEL553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8 +PzGn0EdzMzkbzE5q10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30 +sPDst2yC7S8xmUJMqbINuBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg== +-----END CERTIFICATE----- + +Entrust.net_Global_Secure_Personal_CA +-----BEGIN CERTIFICATE----- +MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NB +X0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT +HChjKSAyMDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1 +c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAy +MDcxNjE2NDBaFw0yMDAyMDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0 +Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29y +cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg +RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xp +ZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7NySpj10InJrWPNTTVRaoTU +rcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0iJBeAZfv6lOm3fzB +3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn5JVn1j+SgF7y +NH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHdBgNVHR8E +gdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUAw +PgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy +ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0 +Lm5ldCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw +IoAPMjAwMDAyMDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQD +AgEGMB8GA1UdIwQYMBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQW +BBSEi3T9xY3A/ydtIDdFfP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2 +fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWA +O9GK9Q6nIMstZVXQkvTnhLUGJoMShAusO7JE7r3PQNsgDrpuFOow4DtifH+L +a3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/GpsKkMWr2tGzhtQvJFJcem3G8v7l +TRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKdzmVml64mXg== +-----END CERTIFICATE----- + +Entrust.net_Global_Secure_Server_CA +-----BEGIN CERTIFICATE----- +MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xf +Q1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVz +dC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wMDAyMDQxNzIwMDBaFw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtF +bnRydXN0Lm5ldDE/MD0GA1UECxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMg +aW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykg +MjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5l +dCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0G +CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO8GCGD9JYf9Mzly0XonUw +tZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaBbL3+qPZ1V1eMkGxK +wz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2dWcTC5/oVzbI +XQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4QgEBBAQD +AgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoTC0Vu +dHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAy +MDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0 +IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV +BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIw +NDE3NTAwMFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc +/vuLkpyw8m4iMB0GA1UdDgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNV +HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkq +hkiG9w0BAQQFAAOBgQBi24GRzsiad0Iv7L0no1MPUBvqTpLwqa+poLpIYcvv +yQbvH9X07t9WLebKahlzqlO+krNQAraFJnJj2HVQYnUUt7NQGj/KEQALhUVp +bbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1UyrrJzOCE98g+EZfTYAkYvAX/ +bIkz8OwVDw== +-----END CERTIFICATE----- + +Entrust.net_Premium_2048_Secure_Server_CA +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNf +MjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT +HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1 +c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEy +MjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0 +Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29y +cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkg +RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4 +QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC +DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj +/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLP +KQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZd +enoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB +0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJ +FrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B +AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFh +fGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVU +KcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaoho +wXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2 ++z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof888 +6ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ== +-----END CERTIFICATE----- + +Entrust.net_Secure_Personal_CA +-----BEGIN CERTIFICATE----- +MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE +BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50 +cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs +aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp +bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa +MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV +BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw +LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50 +cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL +ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv +x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV +iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173 +iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw +ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50 +cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff +SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE +CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50 +cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD +VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D +bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx +MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW +/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG +A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ +OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU +ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE +PHayXOw= +-----END CERTIFICATE----- + +Entrust.net_Secure_Server_CA +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UE +BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50 +cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UE +AxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3 +dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlh +Yi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTow +OAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0 +VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHIN +iC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHk +mGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHT +MBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHY +pIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChs +aW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBM +aW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNo +dHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAi +gA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYE +FPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9 +B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKn +CqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2Zcgx +xufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6 +rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Equifax_Secure_CA +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG +EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1 +cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4 +MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgx +LTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0 +eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2R +FGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO +/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuv +K9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRp +MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEt +MCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjAL +BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9Qw +HQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8w +GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB +AFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2u +FHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +Equifax_Secure_eBusiness_CA_1 +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJV +UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1 +aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcN +MjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZh +eCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2lu +ZXNzIENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fe +k6lfWg0XTzQaDJj0ItlZ1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5 +/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4aIZX5UkxVWsUPOE9G+m34LjXW +HXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBkMBEGCWCG +SAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4 +MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBq +R3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnm +JXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+WB5Hh1Q+WKG1 +tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+KpYr +tWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +Equifax_Secure_eBusiness_CA_2 +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlm +YXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5 +MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXgg +U2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0Et +MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF +7Y6yEb3+6+e0dMKP/wXn2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKD +pkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HM +HMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAGA1UdHwRp +MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBT +ZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y +MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjAL +BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBqy/3YIHqngnYw +HQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMBAf8w +GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB +AAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy +0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkt +y3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN +-----END CERTIFICATE----- + +Equifax_Secure_Global_eBusiness_CA +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJV +UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1 +aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0 +MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoT +E0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJl +IEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQy +td4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORR +OhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2EC +AwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8w +HwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6o +oHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf +2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxa +z2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1 +pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +GeoTrust_Global_CA +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYT +AlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVz +dCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBC +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE +AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEH +CIjaWC9mOSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlC +GDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7 +csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAj +Nvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdRe +JivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQAB +o1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9 +qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjANBgkq +hkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Qzxpe +R+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWV +Yrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot +2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeX +xx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GlobalSign_Root_CA +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzEL +MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV +BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05 +ODA5MDExMjAwMDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkw +FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRsw +GQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR +4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc +71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4 +bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgK +OOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMW +ea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQUYHtmGkUNl8qJ +UC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOC +AQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq75bCd +PTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q +gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT +2iHRrH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlD +NPYPhyk7ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBg +Hcl5JLL2bP2oZg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w== +-----END CERTIFICATE----- + +GTE_CyberTrust_Global_Root +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw +FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy +dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg +R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1 +MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD +VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT +GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4 +ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn +ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F +LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3 +46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq +81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d +XIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +GTE_CyberTrust_Root_CA +-----BEGIN CERTIFICATE----- +MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgw +FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRy +dXN0IFJvb3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQsw +CQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQD +ExNHVEUgQ3liZXJUcnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8K +DPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPw +KfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44AwID +AQABMA0GCSqGSIb3DQEBBAUAA4GBABKzdcZfHeFhVYAA1IFLezEPI2PnPfMD ++fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWlIjeaY8JIILTbcuPI9tl8vrGv +U9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9ApybW1EDp3zdHSo1TRJ6V6e +6bR64eVaH4QwnNOfpSXY +-----END CERTIFICATE----- + +IPS_Chained_CAs_root +-----BEGIN CERTIFICATE----- +MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARwxCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjEzMDEGA1UECxMqSVBTIENBIENoYWluZWQgQ0FzIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MTMwMQYDVQQDEypJUFMgQ0EgQ2hhaW5lZCBDQXMgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlw +cy5lczAeFw0wMTEyMjkwMDUzNThaFw0yNTEyMjcwMDUzNThaMIIBHDELMAkG +A1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vs +b25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBTZXJ2aWNl +cyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBCLTYw +OTI5NDUyMTMwMQYDVQQLEypJUFMgQ0EgQ2hhaW5lZCBDQXMgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkxMzAxBgNVBAMTKklQUyBDQSBDaGFpbmVkIENBcyBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1h +aWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcVpJJspQg +vJhPUOtopKdJC7/SMejHT8KGC/po/UNaivNgkjWZOLtNA1IhW/A3mTXhQSCB +hYEFcYGdtJUZqV92NC5jNzVXjrQfQj8VXOF6wV8TGDIxya2+o8eDZh65nAQT +y2nBBt4wBrszo7Uf8I9vzv+W6FS+ZoCua9tBhDaiPQIDAQABo4IEQzCCBD8w +HQYDVR0OBBYEFKGtMbH5PuEXpsirNPxShwkeYlJBMIIBTgYDVR0jBIIBRTCC +AUGAFKGtMbH5PuEXpsirNPxShwkeYlJBoYIBJKSCASAwggEcMQswCQYDVQQG +EwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmEx +LjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMu +bC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0 +NTIxMzAxBgNVBAsTKklQUyBDQSBDaGFpbmVkIENBcyBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTEzMDEGA1UEAxMqSVBTIENBIENoYWluZWQgQ0FzIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5p +cHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRk +MGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr +BgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYK +KwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBz +QG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlwcy5lczBCBglg +hkgBhvhCAQ0ENRYzQ2hhaW5lZCBDQSBDZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkg +aHR0cDovL3d3dy5pcHMuZXMvMCkGCWCGSAGG+EIBAgQcFhpodHRwOi8vd3d3 +Lmlwcy5lcy9pcHMyMDAyLzA3BglghkgBhvhCAQQEKhYoaHR0cDovL3d3dy5p +cHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0FDLmNybDA8BglghkgBhvhCAQMELxYt +aHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9yZXZvY2F0aW9uQ0FDLmh0bWw/ +MDkGCWCGSAGG+EIBBwQsFipodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl +bmV3YWxDQUMuaHRtbD8wNwYJYIZIAYb4QgEIBCoWKGh0dHA6Ly93d3cuaXBz +LmVzL2lwczIwMDIvcG9saWN5Q0FDLmh0bWwwbQYDVR0fBGYwZDAuoCygKoYo +aHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0FDLmNybDAyoDCg +LoYsaHR0cDovL3d3d2JhY2suaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNBQy5j +cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p +cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAERyMJ1WWKJBGyi3leGmGpVfp3hA +K+/blkr8THFj2XOVvQLiogbHvpcqk4A0hgP63Ng9HgfNHnNDJGD1HWHc3Jag +vPsd4+cSACczAsDAK1M92GsDgaPb1pOVIO/Tln4mkImcJpvNb2ar7QMiRDjM +Wb2f2/YHogF/JsRj9SVCXmK9 +-----END CERTIFICATE----- + +IPS_CLASE1_root +-----BEGIN CERTIFICATE----- +MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjEuMCwGA1UECxMlSVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTEuMCwGA1UEAxMlSVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAx +MTIyOTAwNTkzOFoXDTI1MTIyNzAwNTkzOFowggESMQswCQYDVQQGEwJFUzES +MBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNV +BAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzAp +BgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxLjAs +BgNVBAsTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +LjAsBgNVBAMTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEA4FEnpwvdr9G5Q1uCN0VWcu+atsIS7ywSzHb5 +BlmvXSHU0lq4oNTzav3KaY1mSPd05u42veiWkXWmcSjK5yISMmmwPh5r9FBS +YmL9Yzt9fuzuOOpi9GyocY3h6YvJP8a1zZRCb92CRTzo3wno7wpVqVZHYUxJ +ZHMQKD/Kvwn/xi8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBTrsxl588GlHKzc +uh9morKbadB4CDCCAUQGA1UdIwSCATswggE3gBTrsxl588GlHKzcuh9morKb +adB4CKGCARqkggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNl +bG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJu +ZXQgcHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFp +bC5pcHMuZXMgQy5JLkYuICBCLTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0Eg +Q0xBU0UxIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMg +Q0EgQ0xBU0UxIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcN +AQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8E +BQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH +AwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIB +FgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcw +GgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0Bt +YWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UxIENBIENlcnRpZmlj +YXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC +BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQt +FitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTEuY3Js +MD8GCWCGSAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl +dm9jYXRpb25DTEFTRTEuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93 +d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFMS5odG1sPzA6BglghkgB +hvhCAQgELRYraHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFT +RTEuaHRtbDBzBgNVHR8EbDBqMDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9p +cHMyMDAyL2lwczIwMDJDTEFTRTEuY3JsMDWgM6Axhi9odHRwOi8vd3d3YmFj +ay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0UxLmNybDAvBggrBgEFBQcB +AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZI +hvcNAQEFBQADgYEAK9Dr/drIyllq2tPMMi7JVBuKYn4VLenZMdMu9Ccj/1ur +xUq2ckCuU3T0vAW0xtnIyXf7t/k0f3gA+Nak5FI/LEpjV4F1Wo7ojPsCwJTG +Kbqz3Bzosq/SLmJbGqmODszFV0VRFOlOHIilkfSj945RyKm+hjM+5i9Ibq9U +kE6tsSU= +-----END CERTIFICATE----- + +IPS_CLASE3_root +-----BEGIN CERTIFICATE----- +MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjEuMCwGA1UECxMlSVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTEuMCwGA1UEAxMlSVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAx +MTIyOTAxMDE0NFoXDTI1MTIyNzAxMDE0NFowggESMQswCQYDVQQGEwJFUzES +MBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNV +BAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzAp +BgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxLjAs +BgNVBAsTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +LjAsBgNVBAMTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEAqxf+DrDGaBtT8FK+n/ra+osTBLsBjzLZH49N +zjaY2uQARIwo2BNEKqRrThckQpzTiKRBgtYj+4vJhuW5qYIF3PHeH+AMmVWY +8jjsbJ0gA8DvqqPGZARRLXgNo9KoOtYkTOmWehisEyMiG3zoMRGzXwmqMHBx +RiVrSXGAK5UBsh8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBS4k/8uy9wsjqLn +ev42USGjmFsMNDCCAUQGA1UdIwSCATswggE3gBS4k/8uy9wsjqLnev42USGj +mFsMNKGCARqkggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNl +bG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJu +ZXQgcHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFp +bC5pcHMuZXMgQy5JLkYuICBCLTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0Eg +Q0xBU0UzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMg +Q0EgQ0xBU0UzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcN +AQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8E +BQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH +AwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIB +FgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcw +GgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0Bt +YWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UzIENBIENlcnRpZmlj +YXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC +BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQt +FitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTMuY3Js +MD8GCWCGSAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl +dm9jYXRpb25DTEFTRTMuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93 +d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFMy5odG1sPzA6BglghkgB +hvhCAQgELRYraHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFT +RTMuaHRtbDBzBgNVHR8EbDBqMDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9p +cHMyMDAyL2lwczIwMDJDTEFTRTMuY3JsMDWgM6Axhi9odHRwOi8vd3d3YmFj +ay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0UzLmNybDAvBggrBgEFBQcB +AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZI +hvcNAQEFBQADgYEAF2VcmZVDAyevJuXr0LMXI/dDqsfwfewPxqmurpYPdikc +4gYtfibFPPqhwYHOU7BC0ZdXGhd+pFFhxu7pXu8Fuuu9D6eSb9ijBmgpjnn1 +/7/5p6/ksc7C0YBCJwUENPjDfxZ4IwwHJPJGR607VNCv1TGyr33I6unUVtkO +E7LFRVA= +-----END CERTIFICATE----- + +IPS_CLASEA1_root +-----BEGIN CERTIFICATE----- +MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcN +MDExMjI5MDEwNTMyWhcNMjUxMjI3MDEwNTMyWjCCARQxCzAJBgNVBAYTAkVT +MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwG +A1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjEr +MCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEv +MC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBALsw19zQVL01Tp/FTILq0VA8R5j8m2md +d81u4D/u6zJfX5/S0HnllXNEITLgCtud186Nq1KLK3jgm1t99P1tCeWu4Wwd +ByOgF9H5fahGRpEiqLJpxq339fWUoTCUvQDMRH/uxJ7JweaPCjbB/SQ9AaD1 +e+J8eGZDi09Z8pvZ+kmzAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUZyaW56G/ +2LUDnf473P7yiuYV3TAwggFGBgNVHSMEggE9MIIBOYAUZyaW56G/2LUDnf47 +3P7yiuYV3TChggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlC +YXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIElu +dGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBz +QG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBT +IENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT +JklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJ +KoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAM +BgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYI +KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYB +BAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEE +BAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB +D2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMSBDQSBD +ZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG +SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgB +hvhCAQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xB +U0VBMS5jcmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lw +czIwMDIvcmV2b2NhdGlvbkNMQVNFQTEuaHRtbD8wPQYJYIZIAYb4QgEHBDAW +Lmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTEuaHRt +bD8wOwYJYIZIAYb4QgEIBC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIv +cG9saWN5Q0xBU0VBMS5odG1sMHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93 +d3cuaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNMQVNFQTEuY3JsMDagNKAyhjBo +dHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMS5j +cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p +cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAH66iqyAAIQVCtWYUQxkxZwCWINm +yq0eB81+atqAB98DNEock8RLWCA1NnHtogo1EqWmZaeFaQoO42Hu6r4okzPV +7Oi+xNtff6j5YzHIa5biKcJboOeXNp13XjFr/tOn2yrb25aLH2betgPAK7N4 +1lUH5Y85UN4HI3LmvSAUS7SG +-----END CERTIFICATE----- + +IPS_CLASEA3_root +-----BEGIN CERTIFICATE----- +MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcN +MDExMjI5MDEwNzUwWhcNMjUxMjI3MDEwNzUwWjCCARQxCzAJBgNVBAYTAkVT +MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwG +A1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjEr +MCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEv +MC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAO6AAPYaZC6tasiDsYun7o/ZttvNG7uG +BiJ2MwwSbUhWYdLcgiViL5/SaTBlA0IjWLxH3GvWdV0XPOH/8lhneaDBgbHU +VqLyjRGZ/fZ98cfEXgIqmuJKtROKAP2Md4bm15T1IHUuDky/dMQ/gT6DtKM4 +Ninn6Cr1jIhBqoCm42zvAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUHp9XUEe2 +YZM50yz82l09BXW3mQIwggFGBgNVHSMEggE9MIIBOYAUHp9XUEe2YZM50yz8 +2l09BXW3mQKhggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlC +YXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIElu +dGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBz +QG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBT +IENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT +JklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJ +KoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAM +BgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYI +KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYB +BAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEE +BAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB +D2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMyBDQSBD +ZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG +SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgB +hvhCAQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xB +U0VBMy5jcmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lw +czIwMDIvcmV2b2NhdGlvbkNMQVNFQTMuaHRtbD8wPQYJYIZIAYb4QgEHBDAW +Lmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTMuaHRt +bD8wOwYJYIZIAYb4QgEIBC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIv +cG9saWN5Q0xBU0VBMy5odG1sMHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93 +d3cuaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNMQVNFQTMuY3JsMDagNKAyhjBo +dHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMy5j +cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p +cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAEo9IEca2on0eisxeewBwMwB9dbB +/MjD81ACUZBYKp/nNQlbMAqBACVHr9QPDp5gJqiVp4MI3y2s6Q73nMify5NF +8bpqxmdRSmlPa/59Cy9SKcJQrSRE7SOzSMtEQMEDlQwKeAYSAfWRMS1Jjbs/ +RU4s4OjNtckUFQzjB4ObJnXv +-----END CERTIFICATE----- + +IPS_Servidores_root +-----BEGIN CERTIFICATE----- +MIICtzCCAiACAQAwDQYJKoZIhvcNAQEEBQAwgaMxCzAJBgNVBAYTAkVTMRIw +EAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UE +ChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVz +MRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBz +QG1haWwuaXBzLmVzMB4XDTk4MDEwMTIzMjEwN1oXDTA5MTIyOTIzMjEwN1ow +gaMxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcT +CUJBUkNFTE9OQTEZMBcGA1UEChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UE +CxMPQ2VydGlmaWNhY2lvbmVzMRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEe +MBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyYXZhkJAk8IbPMGbWOlI6H0fg3 +PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1gf/+rHhwLWjhOgeYlQJU +3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4Nu+z4cYgjui0OLzh +PvYR3oydAQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBACzzw3lYJN7GO9HgQmm4 +7mSzPWIBubOE3yN93ZjPEKn+ANgilgUTB1RXxafey9m4iEL2mdsUdx+2/iU9 +4aI+A6mB0i1sR/WWRowiq8jMDQ6XXotBtDvECgZAHd1G9AHduoIuPD14cJ58 +GNCr+Lh3B0Zx8coLY1xq+XKU1QFPoNtC +-----END CERTIFICATE----- + +IPS_Timestamping_root +-----BEGIN CERTIFICATE----- +MIIIODCCB6GgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCAR4xCzAJBgNVBAYT +AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu +MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s +LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1 +MjE0MDIGA1UECxMrSVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTE0MDIGA1UEAxMrSVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwu +aXBzLmVzMB4XDTAxMTIyOTAxMTAxOFoXDTI1MTIyNzAxMTAxOFowggEeMQsw +CQYDVQQGEwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJj +ZWxvbmExLjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZp +Y2VzIHMubC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEIt +NjA5Mjk0NTIxNDAyBgNVBAsTK0lQUyBDQSBUaW1lc3RhbXBpbmcgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkxNDAyBgNVBAMTK0lQUyBDQSBUaW1lc3RhbXBp +bmcgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lw +c0BtYWlsLmlwcy5lczCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLju +VqWajOY2ycJioGaBjRrVetJznw6EZLqVtJCneK/K/lRhW86yIFcBrkSSQxA4 +Efdo/BdApWgnMjvEp+ZCccWZ73b/K5Uk9UmSGGjKALWkWi9uy9YbLA1UZ2t6 +KaFYq6JaANZbuxjC3/YeE1Z2m6Vo4pjOxgOKNNtMg0GmqaMCAwEAAaOCBIAw +ggR8MB0GA1UdDgQWBBSL0BBQCYHynQnVDmB4AyKiP8jKZjCCAVAGA1UdIwSC +AUcwggFDgBSL0BBQCYHynQnVDmB4AyKiP8jKZqGCASakggEiMIIBHjELMAkG +A1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vs +b25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBTZXJ2aWNl +cyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBCLTYw +OTI5NDUyMTQwMgYDVQQLEytJUFMgQ0EgVGltZXN0YW1waW5nIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MTQwMgYDVQQDEytJUFMgQ0EgVGltZXN0YW1waW5n +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNA +bWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsG +A1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUF +BwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGC +NwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMw +EYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlwcy5l +czBHBglghkgBhvhCAQ0EOhY4VGltZXN0YW1waW5nIENBIENlcnRpZmljYXRl +IGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgECBBwW +Gmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMEAGCWCGSAGG+EIBBAQzFjFo +dHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJUaW1lc3RhbXBpbmcu +Y3JsMEUGCWCGSAGG+EIBAwQ4FjZodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAy +L3Jldm9jYXRpb25UaW1lc3RhbXBpbmcuaHRtbD8wQgYJYIZIAYb4QgEHBDUW +M2h0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbFRpbWVzdGFtcGlu +Zy5odG1sPzBABglghkgBhvhCAQgEMxYxaHR0cDovL3d3dy5pcHMuZXMvaXBz +MjAwMi9wb2xpY3lUaW1lc3RhbXBpbmcuaHRtbDB/BgNVHR8EeDB2MDegNaAz +hjFodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJUaW1lc3RhbXBp +bmcuY3JsMDugOaA3hjVodHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9p +cHMyMDAyVGltZXN0YW1waW5nLmNybDAvBggrBgEFBQcBAQQjMCEwHwYIKwYB +BQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZIhvcNAQEFBQADgYEA +ZbrBzAAalZHK6Ww6vzoeFAh8+4Pua2JR0zORtWB5fgTYXXk36MNbsMRnLWha +sl8OCvrNPzpFoeo2zyYepxEoxZSPhExTCMWTs/zif/WN87GphV+I3pGW7hdb +rqXqcGV4LCFkAZXOzkw+UPS2Wctjjba9GNSHSl/c7+lW8AoM6HU= +-----END CERTIFICATE----- + +QuoVadis_Root_CA +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQG +EwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNa +Fw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9W +YWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1 +lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMukJ0KX0J+D +isPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj18 +2d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Sp +x2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZ +yH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospUxbF6lR1xHkop +igPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4wPQYI +KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFk +aXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlh +bmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBw +YXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRp +ZmljYXRpb24gcHJhY3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmlj +YXRlIFBvbGljeS4wIgYIKwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMu +Ym0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYw +gaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCLMA4GA1UdDwEB/wQEAwIB +BjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lofFIk3Wdv +OXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10 +buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe +/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6 +isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQ +NiOKSnQ2+Q== +-----END CERTIFICATE----- + +RSA_Root_Certificate_1 +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD +ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu +Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRp +b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv +bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy +NjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x +NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24g +QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x +IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2f +NUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChM +MFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34 +t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/b +e0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0Wu +PIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +RSA_Security_1024_v3 +-----BEGIN CERTIFICATE----- +MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUF +ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg +U2VjdXJpdHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAx +NDlaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT +QSBTZWN1cml0eSAxMDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDV3f5mCc8kPD6ugU5OisRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dY +rIMKo1W1exeQFYRMiu4mmdxY78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYt +bzZUaMjShFbuklNhCbM/OZuoyZu9zp9+1BlqFikYvtc6adwlWzMaUQIDAQAB +o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSME +GDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAdBgNVHQ4EFgQUxMAcpAeU/c1N +AdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEAPy1q4yZDlX2Jl2X7deRy +HUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdNT1+nr6JGFLkM88y9 +am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgDmMrzVcydro7B +qkWY+o8aoI2II/EVQQ2lRj6RP4vr93E= +-----END CERTIFICATE----- + +RSA_Security_2048_v3 +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUF +ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg +U2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5 +MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT +QSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37 +RqtBaB4Y6lXIL5F4iSj7Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E +0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J +6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iHKrtjEAMq +s6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzD +uvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2Mw +YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW +gBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NRMKSq6UWuNST6 +/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmYv/3V +EhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5g +EydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+ +f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJq +aHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEk +llgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +Security_Communication_Root_CA +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJK +UDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0 +eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMw +OTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1 +c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RD +QTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8 +V6UMbXaKL0u/ZPtM7orw8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpx +xpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uMDPpVmDvY6CKhS3E4eayXkmmz +iX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX5HA49LY6 +tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819 +uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/L +TX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZ +aNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g0dNq +/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94 +nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNn +PaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfci +oU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera_Class_1_Root_CA +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJG +STEPMA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENB +MB4XDTAxMDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMC +RkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue ++H887dF+2rDNbS82rDTG29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mX +y47vPxVnqIJyY1MPQYx9EJUkoVqlBvqSV536pQHydekfvFYmUk54GWVYVQNY +wBSujHxVX3BbdyMGNpfzJLWaRpXk3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JF +hfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBLqdReLjVQCfOAl/QMF6452F/NM8Ec +yonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIINnvmLVz5MxxftLItyM19yejhW +1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB +/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuXZfsSZ9gqXLar +5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0HDjxVyhbM +p6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VOTzF2 +nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv +kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2y +Ix4wzMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa +-----END CERTIFICATE----- + +Sonera_Class_2_Root_CA +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJG +STEPMA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENB +MB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMC +RkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE ++hY3/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gX +GM2RX/uJ4+q/Tl18GybTdXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQ +TiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMGf+dJQMjFAbJUWmYdPfz56TwK +noG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8PtOFCx4j1 +P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURr +BGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB +/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zilzqsWuasvfDXL +rNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEIcbCd +jdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr4 +50kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkeja +nZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +Staat_der_Nederlanden_Root_CA +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG +EwJOTDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQD +Ex1TdGFhdCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIz +NDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVT +dGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRl +cmxhbmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAmNK1URF6gaYUmHFtvsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rF +DBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02PjLwYdjeFnejKScfST5gTCaI+ +Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGcaC1Hoi6Ce +UJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7l +r7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4Zl +kuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGO +MAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDwwOgYIKwYBBQUH +AgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9vdC1w +b2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg +0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k +/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVF +IGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0 +C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBp +IzlWYGeQiy52OfsRiJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYV +wSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +StartCom_Ltd__Free_SSL_Certification_Authority +-----BEGIN CERTIFICATE----- +MIIFFjCCBH+gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsDELMAkGA1UEBhMCSUwx +DzANBgNVBAgTBklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0 +Q29tIEx0ZC4xGjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS +YWRtaW5Ac3RhcnRjb20ub3JnMB4XDTA1MDMxNzE3Mzc0OFoXDTM1MDMxMDE3Mzc0 +OFowgbAxCzAJBgNVBAYTAklMMQ8wDQYDVQQIEwZJc3JhZWwxDjAMBgNVBAcTBUVp +bGF0MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMRowGAYDVQQLExFDQSBBdXRob3Jp +dHkgRGVwLjEpMCcGA1UEAxMgRnJlZSBTU0wgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkxITAfBgkqhkiG9w0BCQEWEmFkbWluQHN0YXJ0Y29tLm9yZzCBnzANBgkqhkiG +9w0BAQEFAAOBjQAwgYkCgYEA7YRgACOeyEpRKSfeOqE5tWmrCbIvNP1h3D3TsM+x +18LEwrHkllbEvqoUDufMOlDIOmKdw6OsWXuO7lUaHEe+o5c5s7XvIywI6Nivcy+5 +yYPo7QAPyHWlLzRMGOh2iCNJitu27Wjaw7ViKUylS7eYtAkUEKD4/mJ2IhULpNYI +LzUCAwEAAaOCAjwwggI4MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgHmMB0G +A1UdDgQWBBQcicOWzL3+MtUNjIExtpidjShkjTCB3QYDVR0jBIHVMIHSgBQcicOW +zL3+MtUNjIExtpidjShkjaGBtqSBszCBsDELMAkGA1UEBhMCSUwxDzANBgNVBAgT +BklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4x +GjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBGcmVlIFNTTCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYSYWRtaW5Ac3Rh +cnRjb20ub3JnggEAMB0GA1UdEQQWMBSBEmFkbWluQHN0YXJ0Y29tLm9yZzAdBgNV +HRIEFjAUgRJhZG1pbkBzdGFydGNvbS5vcmcwEQYJYIZIAYb4QgEBBAQDAgAHMC8G +CWCGSAGG+EIBDQQiFiBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAy +BglghkgBhvhCAQQEJRYjaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL2NhLWNybC5j +cmwwKAYJYIZIAYb4QgECBBsWGWh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy8wOQYJ +YIZIAYb4QgEIBCwWKmh0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9pbmRleC5waHA/ +YXBwPTExMTANBgkqhkiG9w0BAQQFAAOBgQBscSXhnjSRIe/bbL0BCFaPiNhBOlP1 +ct8nV0t2hPdopP7rPwl+KLhX6h/BquL/lp9JmeaylXOWxkjHXo0Hclb4g4+fd68p +00UOpO6wNnQt8M2YI3s3S9r+UZjEHjQ8iP2ZO1CnwYszx8JSFhKVU2Ui77qLzmLb +cCOxgN8aIDjnfg== +-----END CERTIFICATE----- + +TC_TrustCenter__Germany__Class_2_CA +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT +AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD +VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3 +b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENB +MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe +Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE +RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE +ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y +a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTEp +MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0yAClxgwENv4wB3NrGrTmk +qYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDwTFXlay3HhQswHJJO +gtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8/vhYnvgpjbB7 +zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w +DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+W +LDO/jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xR +T3h2oNmsGb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/Ac +ASZ4smZHcFFk +-----END CERTIFICATE----- + +TC_TrustCenter__Germany__Class_3_CA +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT +AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD +VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3 +b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENB +MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe +Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE +RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE +ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y +a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTEp +MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUFLg2N7KBAahwOJ6ZQkmtQ +GwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGwDtf7pBc9r6tpepYn +v68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDWw1Krj10nnGvA +o+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w +DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy +dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4 +iJIETb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yC +GdHHsbHD2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQS +CdS7kjXvD9s0 +-----END CERTIFICATE----- + +TDC_Internet_Root_CA +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQG +EwJESzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50 +ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTda +MEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNV +BAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4Nr +XceO+YQwzho7+vvOi20jxsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaq +HF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5 +Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc5IogCSEW +Vmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8n +mHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwID +AQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBY +oFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIEludGVybmV0MR0w +GwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JMMTAr +BgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjAL +BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw +HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8w +HQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKs +LtB9KOy282A4aW8+2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7T +mHnaCB4Mb7j4Fifvwm899qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE +64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQ +V0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +TDC_OCES_Root_CA +-----BEGIN CERTIFICATE----- +MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQG +EwJESzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0w +MzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQww +CgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSj +hFuHnEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8 +z3sM8W9Hpg1DTeLpHTk0zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJH +hNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvViGjaXbXqzRowwYCDdlCqT9HU +3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBdedObaE+3p +Hx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTw +tyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYB +BQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBvc2l0b3J5MIGd +BggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBmcmEg +ZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x +LiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg +T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEG +A1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERD +MRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm +aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0Q +BCQwIoAPMjAwMzAyMTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0j +BBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4S +GSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDAN +BgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBu +o7E4A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjND +fZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8Aqt +FxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoBmbgGglGBTvH1tJFUuSN6 +AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1AoLbrIyi +gfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2Lq +L19iUw== +-----END CERTIFICATE----- + +Thawte_Personal_Basic_CA +-----BEGIN CERTIFICATE----- +MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du +MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl +cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp +Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow +gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV +BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm +BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV +BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ +cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK +P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ +fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j +kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB +gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7 +c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95 +B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ== +-----END CERTIFICATE----- + +Thawte_Personal_Freemail_CA +-----BEGIN CERTIFICATE----- +MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du +MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl +cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m +cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz +NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx +EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp +bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x +JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG +SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N +j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef +QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY +x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP +MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC +neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr +5PjRzneigQ== +-----END CERTIFICATE----- + +Thawte_Personal_Premium_CA +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du +MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl +cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy +ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5 +NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw +EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n +MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw +IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3 +DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7 +7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j +Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq +W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH +b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx +eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1 +KzGJ +-----END CERTIFICATE----- + +Thawte_Premium_Server_CA +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du +MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy +dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl +IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl +cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1 +OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ +BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg +Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x +ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3 +DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI +NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL +lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN +9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ +a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU +Qg== +-----END CERTIFICATE----- + +Thawte_Server_CA +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du +MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy +dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl +IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG +A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw +ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE +CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ +VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz +QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I +/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC +6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX +TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD +TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni +TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte_Time_Stamping_CA +-----BEGIN CERTIFICATE----- +MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMC +WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmls +bGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmlj +YXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcw +MTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT +BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN +BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x +HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8 +WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR +5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7 +X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK +0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8 +zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJ +SdM= +-----END CERTIFICATE----- + +UTN_DATACorp_SGC_Root_CA +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUF +ADCBkzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw +HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVU +TiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2 +MzBaMIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNh +bHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx +ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEbMBkGA1UEAxMS +VVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+O +GQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrr +U0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrL +Z9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykqlXvY8qdOD1R8 +oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv33i+ +Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4Gr +MIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8v +Y3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUE +IzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHc +rpY6CiM+iVnJowftGzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuM +FrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1 ++XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdO +jtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jF +VkwPDPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN_USERFirst_Email_Root_CA +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUF +ADCBrjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw +HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVU +Ti1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAe +Fw05OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJV +UzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93 +d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGll +bnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxq +mNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqk +kqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6lL8/K2m2qL+us +obNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHGTPNp +saguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZU +Ot4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws +6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0G +A1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJ +hkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGll +bnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEF +BQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rk +MPxTbyUkxsrt4jFcKw7u7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK +2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/ +bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJR +warVNZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf +6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQ +jvLzxq4oW6fw8zYX/MMF08oDSlQ= +-----END CERTIFICATE----- + +UTN_USERFirst_Hardware_Root_CA +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUF +ADCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw +HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVU +Ti1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5 +MTgxOTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQH +Ew5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3 +b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNV +BAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZ +FvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6N +q9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEH +OG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNdoI6yqqr2jmmI +BsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjfPe58 +BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhb +AgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWG +M2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3 +YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0o +XnWO6y1n7k57K9cM//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjA +bPLPSbtNk28GpgoiskliCE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59 +Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4f +Fm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchq +J/kniCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0q +UZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +UTN-USER_First-Network_Applications +-----BEGIN CERTIFICATE----- +MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUF +ADCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw +HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVU +Ti1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0 +ODM5WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgT +AlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVT +RVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVz +dC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNh +dGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZV +hawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAb +GHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZ +NaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hAReYFmnjDRy7rh4 +xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwiP8vv +/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7i +gEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD +AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf +8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0 +LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G +CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXh +i6r/fWRRzwr/vH3YIWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUq +f9FuVSTiuwL7MT++6LzsQCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAf +hZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvP +NximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+ +FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjis +H8SE +-----END CERTIFICATE----- + +UTN_USERFirst_Object_Root_CA +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUF +ADCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw +HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVU +Ti1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4 +NDAzNlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMO +U2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29y +azEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQD +ExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r55 +96Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc +/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2 +yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+pKvEHDHd17bR +5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7RwvC +bNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEA +AaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0 +cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNy +bDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQw +DQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXF +wfNfLEzIR1pp6ujwNTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T +7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85 +dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U ++CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCA +GKCGhU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2o +Ps0AH8g= +-----END CERTIFICATE----- + +ValiCert_Class_1_VA +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD +ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu +Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRp +b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv +bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy +NTIyMjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x +NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24g +QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x +IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw +8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m ++FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSN +dnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwGlN+V +YH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8so +gTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +ValiCert_Class_2_VA +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD +ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu +Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRp +b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv +bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy +NjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x +NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g +QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x +IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc +65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQ +b7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcn +wbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4 +OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZ +oDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +Verisign_Class_1_Public_Primary_Certification_Authority +-----BEGIN CERTIFICATE----- +MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8x +CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UE +CxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNV +BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3 +noaACpEO+jglr0aIguVzqKCbJF0NH8xlbgyw0FaEGIeaBpsQoXPftFg5a27B +9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR4k5FVmkfeAKA2txHkSm7NsljXMXg +1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBM +P7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZoEWx8QszznC7EBz8UsA9P +/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5FvjqBUuUfx3CHMjj +t/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89FxlA== +-----END CERTIFICATE----- + +Verisign_Class_1_Public_Primary_Certification_Authority_-_G2 +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcEx +CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE +CxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt +IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG +A1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j +LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq +0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9 +Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6c +PvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9Zr +bWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul +uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4i +P/68DzFc6PLZ +-----END CERTIFICATE----- + +Verisign_Class_1_Public_Primary_Certification_Authority_-_G3 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHK +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV +BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5 +IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD +BgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3 +MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s +IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV +BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFBy +aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRR +ZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO8ESlV8dAWB6j +Rx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJrKsh +JlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7P +oBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 +6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHh +v2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQ +BfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N +y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUf +xJM8/XmPBNQ+T+r3ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFM +DSZl4kSAHsef493oCtrspSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5 +SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXV +OBRgmaNL3gaWcSzy27YfpO8/7g== +-----END CERTIFICATE----- + +Verisign_Class_1_Public_Primary_OCSP_Responder +-----BEGIN CERTIFICATE----- +MIIDnjCCAwegAwIBAgIQK2jUo0aexTsoCas4XX8nIDANBgkqhkiG9w0BAQUF +ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1 +BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov +L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAx +IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC57V56Ondfzl86UvzNZPdxtW9qlsZZklWUXS9bLsER +6iaKy6eBPPZaRN56Ey/9WlHZezcmSsAnPwQDalbBgyzhb1upVFAkSsYuekyh +WzdUJCExH6F4GHansXDaItBq/gdiQMb39pt9DAa4S8co5GYjhFHvRreT2IEz +y+U2rMboBQIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT +CE9DU1AgMS0xMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp +Z24uY29tL3BjYTEuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF +BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j +b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG +CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud +EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAHCQ3bjkvlMX +fH8C6dX3i5mTMWCNfuZgayTvYKzSzpHegG0JpNO4OOVEynJeDS3Bd5y9LAN4 +KY2kpXeH9fErJq3MB2w6VFoo4AnzTQoEytRYaQuns/XdAaXn3PAfusFdkI2z +6k/BEVmXarIrE7HarZehs7GgIFvKMquNzxPwHynD +-----END CERTIFICATE----- + +Verisign_Class_2_Public_Primary_Certification_Authority +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL +Ey5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZM +JaLtVRKXxaeAufqDwSCg+i8VDXyhYGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvE +erf4Zh+AVPy3wo5ZShRXRtGak75BkQO7FYCTXOvnzAhsPz6zSvz/S2wj1VCC +JkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAIob +K/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxgJ8pFUs4W7z8GZOeUaHxg +MxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Ncr6Pc5iaAIzy4RHT3 +Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY +-----END CERTIFICATE----- + +Verisign_Class_2_Public_Primary_Certification_Authority_-_G2 +-----BEGIN CERTIFICATE----- +MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHB +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNV +BAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4g +LSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24g +VHJ1c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTla +MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6 +BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNp +Z24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +p4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkf +rbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjwDqL7MWzJ5m+Z +Jwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEAATAN +BgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/ +7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX +rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6x +RnInjBJ7xUS0rg== +-----END CERTIFICATE----- + +Verisign_Class_2_Public_Primary_Certification_Authority_-_G3 +-----BEGIN CERTIFICATE----- +MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcox +CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UE +CxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkg +VmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMG +A1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcx +NjIzNTk1OVowgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwg +SW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UE +CxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1 +c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJp +bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY8 +1nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDOJxOeBUebMXoT +2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7C9UT +AJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQ +HgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN +qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVC +YQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekh +ktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf +0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydE +p85EXdQbkJgNHkKUsQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377B +MnMiIYtYgXsVkXq642RIsH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab +5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//jGHyJizNdrDPX +p/naOlXJWBD5qu9ats9LS98q +-----END CERTIFICATE----- + +Verisign_Class_2_Public_Primary_OCSP_Responder +-----BEGIN CERTIFICATE----- +MIIDnjCCAwegAwIBAgIQCUYX5h3Y1BygDKBi6HmKpzANBgkqhkiG9w0BAQUF +ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1 +BgNVBAsTLkNsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDAwODAxMDAwMDAwWhcNMDQwNzMxMjM1OTU5WjCBpzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov +L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAy +IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDQymMxYX9ENHwFfQs9apDLeUt3Cj9LxyPlwGItfpx+ +PoiHkdCs6E1Jh6KWkIrdBKUCP4yb6Yn+YqDiWr3I3bR45qVCkwhnAcAgTddc +9F3as+M3plIaLExlTYqH2aij8UlUuzxcgFFoxvtJ/wtVqxXd+5rBuR10DbKM +RF2J/J/5gwIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT +CE9DU1AgMS0yMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp +Z24uY29tL3BjYTIuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF +BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j +b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG +CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud +EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAB99CW4kRnUE +nPMmm+M5bhfvvL2iG9IChIar0ECXLMRDiDcZayKoA3FQnSDcNmAgmnMtc1Vs +WJsswrQ0LHozQsqR2elDr88e4PXEeqs/cmMeqTfhWzuIsxOGgpBXy1f/9Fa+ +It3jl6jhvCJDwt1N2/aBnpIUnjkPE1TegtjAXjSN +-----END CERTIFICATE----- + +Verisign_Class_3_Public_Primary_Certification_Authority +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL +Ey5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf +MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69q +RUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3In +zPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a +/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtM +EivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPw +TtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzk +uxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +Verisign_Class_3_Public_Primary_Certification_Authority_-_G2 +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcEx +CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE +CxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt +IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG +A1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j +LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM +XtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXX +wc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GV +j0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01U +bSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo +1KpYoJ2daZH9 +-----END CERTIFICATE----- + +Verisign_Class_3_Public_Primary_Certification_Authority_-_G3 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHK +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV +BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5 +IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD +BgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3 +MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s +IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV +BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFBy +aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2 +R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2tKmFZpGcmTNDo +vFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUccLwg +TS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+V +k7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJ +OxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my +/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoA +Wii/gt/4uhMdUIaC/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8S +GhJouPtmmRQURVyu565pF4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbb +o27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh +/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign_Class_3_Public_Primary_OCSP_Responder +-----BEGIN CERTIFICATE----- +MIIDojCCAwugAwIBAgIQLpaev7ZibOx76XPM42zBhDANBgkqhkiG9w0BAQUF +ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1 +BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov +L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDx5AgOg7t140jluNum8Lmr6Txix141W9ACVBHYydFW +uXZLuat65s269gwE1n7WsAplrE454/H3LaMlOe+wi8++2wxdbnD0B81w9zrA +PjUW7XiMQ8/CJi5H1oZ9nPG+1mcMIiWkymXmH3p4KC8/BdsEIb/hRWb+PLeC +7Vq4FhW5VQIDAQABo4IBFDCCARAwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT +CE9DU1AgMS0zMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwudmVyaXNp +Z24uY29tL3BjYTMuMS4xLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCTBCBggr +BgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGmJhYkaHR0cDovL29jc3AudmVyaXNp +Z24uY29tL29jc3Avc3RhdHVzMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw +KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL1JQQTAJ +BgNVHRMEAjAAMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQUFAAOBgQAC9lNj +wKke8tCLMzCPSJtMsFa0g3FKvtxQ2PW24AvbvXhP6c8JNNopSZ0Bc1qRkYJU +LBMK03cjzzf8Y96n4/a3tWlFKEnDkdyqRxypiJksBSqNjYr6YuJatwAgXTnE +KMLL/J6oia5bPY4S6jKy/OsU1wkVGsDNG9W1FU5B1ZbjTg== +-----END CERTIFICATE----- + +Verisign_Class_4_Public_Primary_Certification_Authority_-_G2 +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcEx +CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE +CxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt +IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG +A1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j +LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6 +8OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTK +QWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VX +xbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0G +CSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwjcSGI +L4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y +cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckzt +ImRPT8qAkbYp +-----END CERTIFICATE----- + +Verisign_Class_4_Public_Primary_Certification_Authority_-_G3 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHK +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV +BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5 +IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD +BgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3 +MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s +IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV +BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFBy +aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYl +S+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ+mGuqPKljYXC +KtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM8BDc +VHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdL +MEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDD +Zq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1Wr +IhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csK +vE+MW8VLADsfKoKmfjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluP +QSjA1egtTaRezarZ7c7c2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kP +mF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr +9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Verisign_RSA_Secure_Server_CA +-----BEGIN CERTIFICATE----- +MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL +MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu +MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE +BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD +VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb +MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O +LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs +iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC +sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw +4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr +l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1 +g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA== +-----END CERTIFICATE----- + +Verisign_Secure_Server_OCSP_Responder +-----BEGIN CERTIFICATE----- +MIIDnzCCAwygAwIBAgIRAP9F1SddJPuzwjkkU1fhT94wDQYJKoZIhvcNAQEF +BQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5 +LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5MB4XDTAwMDgwNDAwMDAwMFoXDTA0MDgwMzIzNTk1OVowgZ4x +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6 +Ly93d3cudmVyaXNpZ24uY29tL1JQQSAoYykwMDElMCMGA1UEAxMcU2VjdXJl +IFNlcnZlciBPQ1NQIFJlc3BvbmRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAuFGZZIUO7rMKaPC/Y3YdU/X8oXiMM+6f9L452psPTUepjyDoS0S9 +zs17kNEw6JDEJXuJKN699pMd/7n/krWpjeSuzOLDB4Nqo3IQASdiIqY1Jjkt +ns9gDPxHpNfQQninHWzQy08VpykKtJVFxLHnWgnXOZXYHTWewr2zXcEMSx8C +AwEAAaOCAR0wggEZMCAGA1UdEQQZMBekFTATMREwDwYDVQQDEwhPQ1NQIDEt +NDA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLnZlcmlzaWduLmNvbS9S +U0FTZWN1cmVTZXJ2ZXItcC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwkwQgYI +KwYBBQUHAQEENjA0MDIGCCsGAQUFBzABpiYWJGh0dHA6Ly9vY3NwLnZlcmlz +aWduLmNvbS9vY3NwL3N0YXR1czBEBgNVHSAEPTA7MDkGC2CGSAGG+EUBBwEB +MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9SUEEw +CQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEFBQADfgAAsxBT +ZpxJky4xoAJC0lhXfmah/huKYRhQQCweK0Gl1tv/rAgcWgVtAlwqtpZPR9u+ +TtvOzLqGuBjOsRKRX2P380g+zPFNE+RtCZR4AJLLoyCdBgtqoEMHztEZbI8Y +dZqfFzP9qSa44+LewqjEWop/mNYHBmvMVp6GcM7U7w== +-----END CERTIFICATE----- + +Verisign_Time_Stamping_Authority_CA +-----BEGIN CERTIFICATE----- +MIIDzTCCAzagAwIBAgIQU2GyYK7bcY6nlLMTM/QHCTANBgkqhkiG9w0BAQUF +ADCBwTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTww +OgYDVQQLEzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24g +QXV0aG9yaXR5IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmswHhcNMDAwOTI2MDAwMDAwWhcNMTAwOTI1MjM1 +OTU5WjCBpTEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBh +dCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTAwMSwwKgYDVQQD +EyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIEF1dGhvcml0eSBDQTCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEA0hmdZ8IAIVlizrQJIkRpivglWtvtDbc2 +fk7gu5Q+kCWHwmFHKdm9VLhjzCx9abQzNvQ3B5rB3UBU/OB4naCTuQk9I1F/ +RMIUdNsKvsvJMDRAmD7Q1yUQgZS9B0+c1lQn3y6ov8uQjI11S7zi6ESHzeZB +CiVu6PQkAsVSD27smHUCAwEAAaOB3zCB3DAPBgNVHRMECDAGAQH/AgEAMEUG +A1UdIAQ+MDwwOgYMYIZIAYb4RQEHFwEDMCowKAYIKwYBBQUHAgEWHGh0dHBz +Oi8vd3d3LnZlcmlzaWduLmNvbS9ycGEwMQYDVR0fBCowKDAmoCSgIoYgaHR0 +cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwCwYDVR0PBAQDAgEGMEIG +CCsGAQUFBwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJp +c2lnbi5jb20vb2NzcC9zdGF0dXMwDQYJKoZIhvcNAQEFBQADgYEAgnBold+2 +DcIBcBlK0lRWHqzyRUyHuPU163hLBanInTsZIS5wNEqi9YngFXVF5yg3ADQn +Keg3S/LvRJdrF1Eaw1adPBqK9kpGRjeM+sv1ZFo4aC4cw+9wzrhGBha/937n +tag+RaypJXUie28/sJyU58dzq6wf7iWbwBbtt8pb8BQ= +-----END CERTIFICATE----- + +Visa_eCommerce_Root +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUF +ADBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlz +YSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMT +E1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0 +MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UE +CxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAa +BgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh +28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8bRaVK7362 +rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81 +q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtF +Wsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0 +lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaLdXe6YJ2E5/4t +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOC +AQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR +zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKht +cbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGI +xHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/ +hC3euiInlhBx6yLt398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Visa_International_Global_Root_2 +-----BEGIN CERTIFICATE----- +MIIDgDCCAmigAwIBAgICAx4wDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMC +VVMxDTALBgNVBAoTBFZJU0ExLzAtBgNVBAsTJlZpc2EgSW50ZXJuYXRpb25h +bCBTZXJ2aWNlIEFzc29jaWF0aW9uMRIwEAYDVQQDEwlHUCBSb290IDIwHhcN +MDAwODE2MjI1MTAwWhcNMjAwODE1MjM1OTAwWjBhMQswCQYDVQQGEwJVUzEN +MAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNl +cnZpY2UgQXNzb2NpYXRpb24xEjAQBgNVBAMTCUdQIFJvb3QgMjCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkBcLWqxEDwq2omYXkZAPy/mzdZ +DK9vZBv42pWUJGkzEXDK41Z0ohdXZFwgBuHW73G3O/erwWnQSaSxBNf0V2KJ +XLB1LRckaeNCYOTudNargFbYiCjh+20i/SN8RnNPflRzHqgsVVh1t0zzWkWl +Ahr62p3DRcMiXvOL8WAp0sdftAw6UYPvMPjU58fy+pmjIlC++QU3o63tmsPm +7IgbthknGziLgE3sucfFicv8GjLtI/C1AVj59o/ghalMCXI5Etuz9c9OYmTa +xhkVOmMd6RdVoUwiPDQyRvhlV7or7zaMavrZ2UT0qt2E1w0cslSsMoW0ZA3e +QbuxNMYBhjJk1Z8CAwEAAaNCMEAwHQYDVR0OBBYEFJ59SzS/ca3CBfYDdYDO +qU8axCRMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqG +SIb3DQEBBQUAA4IBAQAhpXYUVfmtJ3CPPPTVbMjMCqujmAuKBiPFyWHbmQdp +NSYx/scuhMKZYdQN6X0uEyt8joW2hcdLzzW2LEc9zikv2G+fiRxkk78IvXbQ +kIqUs38oW26sTTMs7WXcFsziza6kPWKSBpUmv9+55CCmc2rBvveURNZNbyoL +axhNdBA2aGpawWqn3TYpjLgwi08hPwAuVDAHOrqK5MOeyti12HvOdUVmB/Rt +Ldh6yumJivIj2C/LbgA2T/vwLwHMD8AiZfSr4k5hLQOCfZEWtTDVFN5ex5D8 +ofyrEK9ca3CnB+8phuiyJccg/ybdd+95RBTEvd07xQObdyPsoOy7Wjm1zK0G +-----END CERTIFICATE----- + +ICA XSF +-----BEGIN CERTIFICATE----- +MIIHADCCBmmgAwIBAgIBFDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCSUwx +DzANBgNVBAgTBklzcmFlbDEOMAwGA1UEBxMFRWlsYXQxFjAUBgNVBAoTDVN0YXJ0 +Q29tIEx0ZC4xGjAYBgNVBAsTEUNBIEF1dGhvcml0eSBEZXAuMSkwJwYDVQQDEyBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJARYS +YWRtaW5Ac3RhcnRjb20ub3JnMB4XDTA2MTIwMjIzNTUyMVoXDTExMTIwMjIzNTUy +MVowgdgxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhDb2xvcmFkbzEjMCEGA1UECgwa +SmFiYmVyIFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAsMGlNlY3VyZSBDZXJ0 +aWZpY2F0ZSBTaWduaW5nMUYwRAYDVQQDDD1TdGFydENvbSBDbGFzcyAxIEludGVy +bWVkaWF0ZSBDQSAtIEphYmJlciBTb2Z0d2FyZSBGb3VuZGF0aW9uMSQwIgYJKoZI +hvcNAQkBFhVjZXJ0bWFzdGVyQGphYmJlci5vcmcwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCeju/E54r6cwRmEzkGwBIq5anE2IHM10iYIeqOjTnN2WMM +XERxgmuSpwJays/BaMATh1/QFnMHjXiTICmeyXbJ2fKrxTHPCJ+DUeLbFvVX3bOO +SxAffkCLwZuUw9RyZ9zDLBNpR1FsdiSD9mV9DEH4T3sNU79Mjy+o83jFojTg39R7 +nH8B6z7VLmlC+ENxsMqjdwRv7HtY595VBLwK/gejblT8kCVFFA/WjmiOVoZ4aMGd +OOvsSgEZ9LaejB4xZdq+PP40DjxqhMQw89uzhWnCxxh0h+4PNfxhbPqJxZ9UMUWg +uPLYPAoj9U5p3YgmRvEaKdrijOkhODeNVkV/a57jAgMBAAGjggN6MIIDdjAMBgNV +HRMEBTADAQH/MAsGA1UdDwQEAwIBJjAdBgNVHQ4EFgQUe47EZ9BGIRcR/6F6QnWf +6sSrcuQwgd0GA1UdIwSB1TCB0oAUHInDlsy9/jLVDYyBMbaYnY0oZI2hgbakgbMw +gbAxCzAJBgNVBAYTAklMMQ8wDQYDVQQIEwZJc3JhZWwxDjAMBgNVBAcTBUVpbGF0 +MRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMRowGAYDVQQLExFDQSBBdXRob3JpdHkg +RGVwLjEpMCcGA1UEAxMgRnJlZSBTU0wgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +ITAfBgkqhkiG9w0BCQEWEmFkbWluQHN0YXJ0Y29tLm9yZ4IBADAgBgNVHREEGTAX +gRVjZXJ0bWFzdGVyQGphYmJlci5vcmcwHQYDVR0SBBYwFIESYWRtaW5Ac3RhcnRj +b20ub3JnMBEGCWCGSAGG+EIBAQQEAwIABzBUBglghkgBhvhCAQ0ERxZFU3RhcnRD +b20gQ2xhc3MgMSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEphYmJlciBTb2Z0 +d2FyZSBGb3VuZGF0aW9uMGIGA1UdHwRbMFkwKaAnoCWGI2h0dHA6Ly9jZXJ0LnN0 +YXJ0Y29tLm9yZy9jYS1jcmwuY3JsMCygKqAohiZodHRwOi8vY3JsLnN0YXJ0Y29t +Lm9yZy9jcmwvY2EtY3JsLmNybDCCAUoGA1UdIASCAUEwggE9MIIBOQYLKwYBBAGB +tTcBAQEwggEoMC8GCCsGAQUFBwIBFiNodHRwOi8vY2VydC5zdGFydGNvbS5vcmcv +cG9saWN5LnBkZjA1BggrBgEFBQcCARYpaHR0cDovL2NlcnQuc3RhcnRjb20ub3Jn +L2ludGVybWVkaWF0ZS5wZGYwgb0GCCsGAQUFBwICMIGwMBQWDVN0YXJ0Q29tIEx0 +ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpM +ZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRj +b20ub3JnL3BvbGljeS5wZGYwDQYJKoZIhvcNAQEFBQADgYEAtOq85Q1lf8PjsJCg +uQ6TL3TJ1rSadfOwEyHJqIjR5LYpxdcJ5WxSEM3DxdrFnTaPBC6RQ7v836i9DdW3 +FS5/y1Et5gKksLNPQqaYEVFuvB4AGTp2HkdUGo8Oz9Dd4zTcvTSTeo/9mVxqdxKa +lhMZMHD/ivqg8faZSQNYMg6xq7I= +-----END CERTIFICATE----- + diff --git a/po/de.po b/po/de.po index a4a641ec6..ec658b229 100644 --- a/po/de.po +++ b/po/de.po @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: gajim 0.11\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2007-03-29 13:38+0200\n" -"PO-Revision-Date: 2007-03-29 13:41+0100\n" -"Last-Translator: Benjamin Drung \n" +"PO-Revision-Date: 2007-08-03 23:11+0100\n" +"Last-Translator: Michael Skiba \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -204,9 +204,8 @@ msgid "Account Modification" msgstr "Kontoänderung" #: ../data/glade/account_modification_window.glade.h:6 -#, fuzzy msgid "Administration operations" -msgstr "Administratorliste" +msgstr "Administrative Aktionen" #: ../data/glade/account_modification_window.glade.h:7 msgid "Auto-reconnect when connection is lost" @@ -379,9 +378,8 @@ msgid "Synch_ronize account status with global status" msgstr "Konto-Status mit globalem _Status abgleichen" #: ../data/glade/account_modification_window.glade.h:42 -#, fuzzy msgid "Synchronise contacts" -msgstr "_Abgemeldete Kontakte anzeigen" +msgstr "Synchronisiere Kontakte" #: ../data/glade/account_modification_window.glade.h:43 msgid "Use _SSL (legacy)" @@ -1039,9 +1037,8 @@ msgid "_Bookmark This Room" msgstr "Raum zu _Lesezeichen hinzufügen" #: ../data/glade/gc_control_popup_menu.glade.h:7 -#, fuzzy msgid "_Destroy room" -msgstr "_Neuer Raum" +msgstr "_Raum zerstören" #: ../data/glade/gc_occupants_menu.glade.h:1 msgid "Mo_derator" @@ -2338,9 +2335,8 @@ msgid "Bulgarian" msgstr "Bulgarisch" #: ../src/chat_control.py:52 -#, fuzzy msgid "Breton" -msgstr "Briton" +msgstr "Bretonisch" #: ../src/chat_control.py:52 msgid "Czech" @@ -2367,7 +2363,6 @@ msgid "Spanish" msgstr "Spanisch" #: ../src/chat_control.py:52 -#, fuzzy msgid "Basque" msgstr "Baskisch" @@ -2752,11 +2747,11 @@ msgstr "Sperrliste" #: ../src/config.py:2180 msgid "Member List" -msgstr "Mitgliedsliste" +msgstr "Mitgliederliste" #: ../src/config.py:2181 msgid "Owner List" -msgstr "Listenbesitzer" +msgstr "Besitzerliste" #: ../src/config.py:2182 msgid "Administrator List" @@ -3203,23 +3198,20 @@ msgid "%s is not the name of a group chat." msgstr "%s ist kein Name eines Gruppenchats." #: ../src/dialogs.py:1327 -#, fuzzy msgid "Without a connection, you can not synchronise your contacts." -msgstr "Sie müssen verbunden sein, um Ihr Passwort zu ändern" +msgstr "Sie müssen verbunden sein, um Ihre Kontakte zu Synchronisieren." #: ../src/dialogs.py:1374 -#, fuzzy msgid "This account is not connected to the server" -msgstr "Konto \"%s\" ist mit Server verbunden" +msgstr "Konto \"%s\" ist nicht mit dem Server verbunden" #: ../src/dialogs.py:1375 -#, fuzzy msgid "You cannot synchronize with an account unless it is connected." -msgstr "Sie können einem Gruppenchat erst beitreten, wenn Sie verbunden sind." +msgstr "Sie können nicht mit einem Konto Synchronisieren, solange es nicht verbunden ist." #: ../src/dialogs.py:1399 msgid "Synchronise" -msgstr "" +msgstr "Synchronisieren" #: ../src/dialogs.py:1457 #, python-format @@ -4557,19 +4549,21 @@ msgstr "Bitte geben Sie an, welchen Spitznamen Sie verwenden möchten:" #. Ask for a reason #: ../src/groupchat_control.py:1457 -#, fuzzy, python-format +#, python-format msgid "Destroying %s" -msgstr "Beschreibung: %s" +msgstr "Zerstöre %s" #: ../src/groupchat_control.py:1458 msgid "" "You are going to definitively destroy this room.\n" "You may specify a reason below:" msgstr "" +"Sie werden den Raum endgültig zerstören.\n" +"Sie können hier einen Grund angeben:" #: ../src/groupchat_control.py:1460 msgid "You may also enter an alternate venue:" -msgstr "" +msgstr "Sie können auch einen alternativen Raum eintragen:" #: ../src/groupchat_control.py:1490 msgid "Bookmark already set" @@ -5161,11 +5155,11 @@ msgid "Metacontacts are a way to regroup several contacts in one line. Generally msgstr "Metakontakte sind eine Möglichkeit, mehrere Kontakte in einer Zeile zu gruppieren. Normalerweise benutzt man Sie, wenn eine Person mehrere Jabber- oder Transport-Konten hat." #: ../src/roster_window.py:4132 -#, fuzzy, python-format +#, python-format msgid "Do you want to send that file to %s:" msgid_plural "Do you want to send those files to %s:" msgstr[0] "%s möchte ihnen eine Datei senden:" -msgstr[1] "%s möchte ihnen eine Datei senden:" +msgstr[1] "%s möchte ihnen diese Datei senden:" #: ../src/roster_window.py:4237 #, python-format @@ -5722,7 +5716,7 @@ msgstr "Kann leer, 'chat' oder 'normal' sein. Wenn nicht leer, dann werden alle #: ../src/common/config.py:227 msgid "If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened." -msgstr "" +msgstr "Wenn aktiviert, wird Gajim automatisch zu der Person springen und auswählen, die die letzte Nachricht geschickt hat, falls das Chat Fenster nicht bereits offen ist." #: ../src/common/config.py:238 msgid "Priority will change automatically according to your status. Priorities are defined in autopriority_* options." @@ -5874,13 +5868,13 @@ msgstr "Falscher Host" #: ../src/common/connection_handlers.py:180 #: ../src/common/zeroconf/connection_handlers_zeroconf.py:236 -#, fuzzy, python-format +#, python-format msgid "The host %s you configured as the ft_add_hosts_to_send advanced option is not valid, so ignored." -msgstr "Der Host, den Sie für die erweiterte Option ft_override_host_to_send angeben haben ist ungültig und wird ignoriert." +msgstr "Der Host %s, den Sie für die erweiterte Option ft_override_host_to_send angeben haben ist ungültig und wird ignoriert." #: ../src/common/connection_handlers.py:216 msgid "Invalid local address? :-O" -msgstr "" +msgstr "Ungültige Lokale Adresse? :-O" #: ../src/common/connection_handlers.py:607 #, python-format @@ -5940,14 +5934,13 @@ msgstr "" #. Room has been destroyed. see #. http://www.xmpp.org/extensions/xep-0045.html#destroyroom #: ../src/common/connection_handlers.py:1676 -#, fuzzy msgid "Room has been destroyed" -msgstr "Autorisierung wurde entfernt" +msgstr "Raum wurde zerstört" #: ../src/common/connection_handlers.py:1683 #, python-format msgid "You can join this room instead: %s" -msgstr "" +msgstr "Sie können stattdessen diesem Raum beitreten: %s" #: ../src/common/connection_handlers.py:1709 msgid "I would like to add you to my roster." @@ -5996,9 +5989,8 @@ msgid "Invalid answer" msgstr "Ungültige Antwort" #: ../src/common/connection.py:379 -#, fuzzy msgid "Connection to proxy failed" -msgstr "Verbindung fehlgeschlagen" +msgstr "Verbindung mit Proxy fehlgeschlagen" #: ../src/common/connection.py:433 #: ../src/common/connection.py:531 diff --git a/po/ru.po b/po/ru.po index b9c68395f..3fc70b80d 100644 --- a/po/ru.po +++ b/po/ru.po @@ -6,24 +6,24 @@ # This file is distributed under the same license as the gajim package. # # +#: ../src/gajim-remote.py:202 ../src/gajim-remote.py:209 # , 2005. # , 2005. -# Yakov Bezrukov , 2005, 2006. +# Yakov Bezrukov , 2005, 2006, 2007. # Yakov Bezrukov , 2005. -#: ../src/gajim-remote.py:202 ../src/gajim-remote.py:209 msgid "" msgstr "" "Project-Id-Version: ru\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-12-18 09:32+0100\n" -"PO-Revision-Date: 2006-11-22 18:17+0300\n" -"Last-Translator: Alex V. Myltsev \n" +"PO-Revision-Date: 2007-07-08 21:53+0700\n" +"Last-Translator: Yakov Bezrukov \n" "Language-Team: Русский \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%" -"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: KBabel 1.11.4\n" #: ../data/gajim.desktop.in.in.h:1 msgid "A GTK+ Jabber client" @@ -62,7 +62,7 @@ msgstr "_Комната" #: ../data/glade/account_context_menu.glade.h:6 #: ../data/glade/zeroconf_context_menu.glade.h:1 msgid "_Modify Account..." -msgstr "_Редактировать учетную запись..." +msgstr "_Изменить учетную запись..." #: ../data/glade/account_context_menu.glade.h:7 msgid "_Open Gmail Inbox" @@ -99,7 +99,7 @@ msgstr "" #: ../data/glade/account_creation_wizard_window.glade.h:7 msgid "Connect when I press Finish" -msgstr "Подключиться, когда я нажму Конец" +msgstr "Подключиться, когда я нажму \"Завершить\"" #: ../data/glade/account_creation_wizard_window.glade.h:8 msgid "Gajim: Account Creation Wizard" @@ -134,7 +134,7 @@ msgstr "Параметры сервера" #: ../data/glade/account_creation_wizard_window.glade.h:15 msgid "Set my profile when I connect" -msgstr "Установить мой профиль при подсоединении" +msgstr "Настроить мой профиль после соединения" #: ../data/glade/account_creation_wizard_window.glade.h:16 msgid "" @@ -151,7 +151,7 @@ msgstr "Ваш JID:" #: ../data/glade/account_creation_wizard_window.glade.h:19 #: ../data/glade/roster_window.glade.h:11 msgid "_Advanced" -msgstr "_Дополнительные действия" +msgstr "_Дополнительно" #: ../data/glade/account_creation_wizard_window.glade.h:20 msgid "_Finish" @@ -160,7 +160,7 @@ msgstr "_Закончить" #: ../data/glade/account_creation_wizard_window.glade.h:21 #: ../data/glade/manage_proxies_window.glade.h:9 msgid "_Host:" -msgstr "_Хост: " +msgstr "_Хост:" #: ../data/glade/account_creation_wizard_window.glade.h:22 #: ../data/glade/account_modification_window.glade.h:47 @@ -321,7 +321,7 @@ msgstr "" #: ../data/glade/account_modification_window.glade.h:23 msgid "Information about you, as stored in the server" -msgstr "Информация о вас, как она хранится на сервере" +msgstr "Информация о вас, сохраненная на сервере" #: ../data/glade/account_modification_window.glade.h:24 msgid "Manage..." @@ -402,7 +402,7 @@ msgstr "Сохранить _парольную фразу (небезопасн #: ../data/glade/account_modification_window.glade.h:37 #: ../data/glade/zeroconf_properties_window.glade.h:19 msgid "Save conversation _logs for all contacts" -msgstr "_История всех контактов для этой учетной записи" +msgstr "_Сохранять историю для всех контактов" #: ../data/glade/account_modification_window.glade.h:39 msgid "Send keep-alive packets" @@ -415,7 +415,7 @@ msgstr "Син_хронизировать статус учетной запис #: ../data/glade/account_modification_window.glade.h:41 msgid "Use _SSL (legacy)" -msgstr "Использовать _SSL (legacy)" +msgstr "Использовать _SSL (устарело)" #: ../data/glade/account_modification_window.glade.h:42 msgid "Use custom hostname/port" @@ -449,6 +449,8 @@ msgid "" "This is only available if python-avahi is installed and avahi-daemon is " "running." msgstr "" +"Если отмечено, то все локальные контакты, которые используют Bonjour-совместимый клиент (напр. iChat, Trillian или Gaim) будут показаны в ростере. Для этого не нужно подключаться к jabber-серверу.\n" +"Доступно только в том случае, когда установлен python-avahi и запущен avahi-daemon." #: ../data/glade/accounts_window.glade.h:4 msgid "" @@ -464,7 +466,7 @@ msgstr "_Объединить учетные записи" #: ../data/glade/accounts_window.glade.h:6 msgid "_Enable link-local messaging" -msgstr "" +msgstr "_Включить локальный обмен сообщениями" #: ../data/glade/accounts_window.glade.h:7 msgid "_Modify" @@ -508,7 +510,7 @@ msgid "" "You must be connected to the transport to be able\n" "to add a contact from this protocol." msgstr "" -"Чтобы добавить контакт этого протокола,\n" +"Чтобы добавить контакт для этого протокола,\n" "нужно соединиться с транспортом." #: ../data/glade/add_new_contact_window.glade.h:11 @@ -537,7 +539,7 @@ msgstr "Произошла ошибка:" #: ../data/glade/adhoc_commands_window.glade.h:2 msgid "Choose command to execute:" -msgstr "Выберите команду:" +msgstr "Выберите команду:" #: ../data/glade/adhoc_commands_window.glade.h:3 msgid "Ad-hoc Commands - Gajim" @@ -545,7 +547,7 @@ msgstr "Специальные команды - Gajim" #: ../data/glade/adhoc_commands_window.glade.h:4 msgid "Check once more" -msgstr "" +msgstr "Проверить еще раз" #: ../data/glade/adhoc_commands_window.glade.h:5 msgid "Error description..." @@ -573,9 +575,7 @@ msgstr "Описание" #: ../data/glade/advanced_configuration_window.glade.h:2 msgid "NOTE: You should restart gajim for some setting to take effect" -msgstr "" -"ПРИМЕЧАНИЕ: Вы должны перезапустить gajim, чтобы некоторые настройки " -"были применены" +msgstr "ПРИМЕЧАНИЕ: Вы должны перезапустить gajim, чтобы некоторые настройки вступили в силу" #: ../data/glade/advanced_configuration_window.glade.h:3 msgid "Advanced Configuration Editor" @@ -587,7 +587,7 @@ msgstr "Фильтр:" #: ../data/glade/advanced_menuitem_menu.glade.h:1 msgid "Delete MOTD" -msgstr "Удалить MOTD" +msgstr "Удалить сообщение дня" #: ../data/glade/advanced_menuitem_menu.glade.h:2 msgid "Deletes Message of the Day" @@ -595,16 +595,15 @@ msgstr "Удаляет сообщение дня." #: ../data/glade/advanced_menuitem_menu.glade.h:3 msgid "Sends a message to currently connected users to this server" -msgstr "" -"Отправляет сообщение пользователям подсоединенным к серверу в данный момент" +msgstr "Отправляет сообщение пользователям подсоединенным к серверу в данный момент" #: ../data/glade/advanced_menuitem_menu.glade.h:4 msgid "Set MOTD" -msgstr "Установить MOTD" +msgstr "Установить собщение дня" #: ../data/glade/advanced_menuitem_menu.glade.h:5 msgid "Sets Message of the Day" -msgstr "Устанавливает фортунку" +msgstr "Устанавливает сообщение дня" #: ../data/glade/advanced_menuitem_menu.glade.h:6 msgid "Show _XML Console" @@ -624,7 +623,7 @@ msgstr "_Администрирование" #: ../data/glade/advanced_menuitem_menu.glade.h:10 msgid "_Privacy Lists" -msgstr "Списки доступа" +msgstr "_Списки доступа" #: ../data/glade/advanced_menuitem_menu.glade.h:11 msgid "_Send Server Message" @@ -636,7 +635,7 @@ msgstr "О_тправить одиночное сообщение" #: ../data/glade/advanced_notifications_window.glade.h:1 msgid " a window/tab opened with that contact " -msgstr "" +msgstr " с этим контактом уже открыты окно или вкладка" #: ../data/glade/advanced_notifications_window.glade.h:2 msgid "Actions" @@ -652,19 +651,16 @@ msgid "Sounds" msgstr "Звуки" #: ../data/glade/advanced_notifications_window.glade.h:5 -#, fuzzy msgid "Advanced Actions" -msgstr "Расширенный" +msgstr "Дополнительные действия" #: ../data/glade/advanced_notifications_window.glade.h:6 -#, fuzzy msgid "Advanced Notifications Control" -msgstr "Расширенный редактор настроек" +msgstr "Дополнительное управление уведомлениями" #: ../data/glade/advanced_notifications_window.glade.h:7 -#, fuzzy msgid "All statuses" -msgstr "Статус: " +msgstr "Все статусы" #: ../data/glade/advanced_notifications_window.glade.h:8 #: ../src/common/helpers.py:234 @@ -672,17 +668,16 @@ msgid "Away" msgstr "Ушел" #: ../data/glade/advanced_notifications_window.glade.h:9 -#, fuzzy msgid "Busy " -msgstr "Занят" +msgstr "Занят " #: ../data/glade/advanced_notifications_window.glade.h:10 msgid "Don't have " -msgstr "" +msgstr "Не имеет " #: ../data/glade/advanced_notifications_window.glade.h:11 msgid "Have " -msgstr "" +msgstr "Имеет " #: ../data/glade/advanced_notifications_window.glade.h:12 #: ../src/common/helpers.py:244 @@ -690,9 +685,8 @@ msgid "Invisible" msgstr "Невидимка" #: ../data/glade/advanced_notifications_window.glade.h:13 -#, fuzzy msgid "Launch a command" -msgstr "команда" +msgstr "Выполнить команду" #: ../data/glade/advanced_notifications_window.glade.h:14 #: ../src/common/helpers.py:217 @@ -701,17 +695,15 @@ msgstr "Недоступен" #: ../data/glade/advanced_notifications_window.glade.h:15 msgid "One or more special statuses..." -msgstr "" +msgstr "Один или несколько специальных статусов..." #: ../data/glade/advanced_notifications_window.glade.h:16 -#, fuzzy msgid "Online / Free For Chat" -msgstr "Готов поболтать" +msgstr "В сети / Готов поболтать" #: ../data/glade/advanced_notifications_window.glade.h:17 -#, fuzzy msgid "Play a sound" -msgstr "Проигрывать _звук" +msgstr "Проигрывать звук" #: ../data/glade/advanced_notifications_window.glade.h:18 msgid "" @@ -725,58 +717,63 @@ msgid "" "File Transfert Started \n" "File Transfert Finished" msgstr "" +"Получено сообщение\n" +"Контакт подключился\n" +"Контакт отключился\n" +"Контакт сменил статус\n" +"Упоминание в комнате\n" +"Сообщение в комнате\n" +"Запрос на передачу файла\n" +"Начата передача файла\n" +"Завершена передача файла" #: ../data/glade/advanced_notifications_window.glade.h:27 msgid "When " -msgstr "" +msgstr "Когда " #: ../data/glade/advanced_notifications_window.glade.h:28 -msgid "" -"_Activate window manager's UrgencyHint to make chat window in taskbar flash" -msgstr "" +msgid "_Activate window manager's UrgencyHint to make chat window in taskbar flash" +msgstr "_Активировать функцию оконного менеджера \"UrgencyHint\", чтобы окно беседы мигало на панели" #: ../data/glade/advanced_notifications_window.glade.h:29 -#, fuzzy msgid "_Disable auto opening chat window" -msgstr "Скрывает кнопки окна комнаты" +msgstr "_Отключить автоматическое открытие окна беседы" #: ../data/glade/advanced_notifications_window.glade.h:30 msgid "_Disable existing popup window" -msgstr "" +msgstr "_Отключиться все всплывающие окна" #: ../data/glade/advanced_notifications_window.glade.h:31 msgid "_Disable existing sound for this event" -msgstr "" +msgstr "_Отключить звуки для этого события" #: ../data/glade/advanced_notifications_window.glade.h:32 msgid "_Disable showing event in roster" -msgstr "" +msgstr "_Отключить показ события в ростере" #: ../data/glade/advanced_notifications_window.glade.h:33 msgid "_Disable showing event in systray" -msgstr "" +msgstr "_Отключить показ события в системном лотке" #: ../data/glade/advanced_notifications_window.glade.h:34 msgid "_Inform me with a popup window" -msgstr "" +msgstr "_Сообщить с помощью всплывающего окна" #: ../data/glade/advanced_notifications_window.glade.h:35 msgid "_Open chat window with user" -msgstr "" +msgstr "_Открыть окно беседы с контактом" #: ../data/glade/advanced_notifications_window.glade.h:36 -#, fuzzy msgid "_Show event in roster" -msgstr "Показывать только в _ростере" +msgstr "_Показывать событие в ростере" #: ../data/glade/advanced_notifications_window.glade.h:37 -#, fuzzy msgid "_Show event in systray" -msgstr "Показывать только в _ростере" +msgstr "_Показывать событие в лотке" #: ../data/glade/advanced_notifications_window.glade.h:38 msgid "and I " -msgstr "" +msgstr "и я" #: ../data/glade/advanced_notifications_window.glade.h:39 msgid "" @@ -784,55 +781,53 @@ msgid "" "group(s)\n" "everybody" msgstr "" +"контакт(ы)\n" +"группа(ы)\n" +"все" #: ../data/glade/advanced_notifications_window.glade.h:42 -#, fuzzy msgid "for " -msgstr "Порт: " +msgstr "для " #: ../data/glade/advanced_notifications_window.glade.h:43 msgid "when I'm in" -msgstr "" +msgstr "Когда я в " #: ../data/glade/atom_entry_window.glade.h:1 msgid "2003-12-13T18:30:02Z" -msgstr "" +msgstr "2003-12-13T18:30:02Z" #: ../data/glade/atom_entry_window.glade.h:2 msgid "Romeo and Juliet" -msgstr "" +msgstr "Иван и Марья" #: ../data/glade/atom_entry_window.glade.h:3 -#, fuzzy msgid "Entry:" -msgstr "Страна:" +msgstr "Запись:" #: ../data/glade/atom_entry_window.glade.h:4 -#, fuzzy msgid "Feed name:" -msgstr "имя тeмы" +msgstr "Название ленты:" #: ../data/glade/atom_entry_window.glade.h:5 -#, fuzzy msgid "Last modified:" -msgstr "Имя:" +msgstr "Последнее изменение:" #: ../data/glade/atom_entry_window.glade.h:6 -#, fuzzy msgid "New entry received" -msgstr "Когда получено новое сообытие" +msgstr "Получена новая запись" #: ../data/glade/atom_entry_window.glade.h:7 msgid "Old stories" -msgstr "" +msgstr "Старые записи" #: ../data/glade/atom_entry_window.glade.h:8 msgid "Soliloquy" -msgstr "" +msgstr "Монолог" #: ../data/glade/atom_entry_window.glade.h:9 msgid "You have received new entry:" -msgstr "" +msgstr "Получена новая запись:" #: ../data/glade/change_password_dialog.glade.h:1 msgid "Change Password" @@ -856,7 +851,7 @@ msgstr "Предустановленные сообщения:" #: ../data/glade/change_status_message_dialog.glade.h:3 msgid "Save as Preset..." -msgstr "Сохранить как предустановленный параметр..." +msgstr "Сохранить как предустановленное сообщение..." #: ../data/glade/chat_context_menu.glade.h:1 msgid "Join _Group Chat" @@ -894,7 +889,7 @@ msgstr "Начать беседу" #: ../data/glade/chat_control_popup_menu.glade.h:1 msgid "Click to see past conversations with this contact" -msgstr "Щелкните для просмотра бесед с этим человеком" +msgstr "Щелкните для просмотра последних бесед с этим человеком" #: ../data/glade/chat_control_popup_menu.glade.h:2 #: ../data/glade/roster_contact_context_menu.glade.h:8 @@ -1017,7 +1012,7 @@ msgstr "" "Учетная запись\n" "Группа\n" "Контакт\n" -"Баннер" +"Заголовок окна беседы" #: ../data/glade/gajim_themes_window.glade.h:6 msgid "Bold" @@ -1134,9 +1129,8 @@ msgid "_Voice" msgstr "_Право говорить" #: ../data/glade/groups_post_window.glade.h:1 -#, fuzzy msgid "Create new post" -msgstr "Новое сообщение" +msgstr "Создать новое сообщение" #: ../data/glade/groups_post_window.glade.h:2 ../src/common/helpers.py:259 msgid "From" @@ -1354,7 +1348,7 @@ msgstr "Настройки GMail" #: ../data/glade/preferences_window.glade.h:7 msgid "Interface Customization" -msgstr "Настройки интерфейса" +msgstr "Настройка интерфейса" #: ../data/glade/preferences_window.glade.h:9 msgid "Preset Status Messages" @@ -1399,7 +1393,7 @@ msgid "" "An example: If you have enabled status message for away, Gajim won't ask you " "anymore for a status message when you change your status to away; it will " "use the default one set here" -msgstr "" +msgstr "Пример: если вы включили статусные сообщения для режима \"Отошел\", Gajim вас больше не будет спрашивать о статусом сообщении, когда вы смените статус на \"Отошел\", поскольку будет использовать значение по-умолчанию, которое установлено здесь" #: ../data/glade/preferences_window.glade.h:21 msgid "Ask status message when I:" @@ -1459,7 +1453,7 @@ msgstr "Тема статусных _иконок:" #: ../data/glade/preferences_window.glade.h:35 msgid "Display _extra email details" -msgstr "" +msgstr "Отображать _дополнительные данные о почте" #: ../data/glade/preferences_window.glade.h:36 msgid "Display a_vatars of contacts in roster" @@ -1470,9 +1464,8 @@ msgid "Display status _messages of contacts in roster" msgstr "Показать _сообщения о статусе контакта в ростере" #: ../data/glade/preferences_window.glade.h:38 -#, fuzzy msgid "Displayed Chat state noti_fications:" -msgstr "_Уведомление об изменении состояния:" +msgstr "Отображать уведомления со_стоянии беседы:" #: ../data/glade/preferences_window.glade.h:39 msgid "E_very 5 minutes" @@ -1487,15 +1480,13 @@ msgid "Events" msgstr "События" #: ../data/glade/preferences_window.glade.h:42 -#, fuzzy msgid "" "Gajim can send and receive meta-information related to a conversation you " "may have with a contact. Here you can specify which chatstates you want to " "display in chat windows." msgstr "" "Gajim может отсылать и получать метаданные относящиеся к беседе с контактом. " -"Здесь вы можете указать какие состояние беседы клиент будет отсылать вашему " -"респонденту." +"Здесь вы можете указать какие состояние беседы вы хотели бы видеть в окне беседы." #: ../data/glade/preferences_window.glade.h:43 msgid "" @@ -1508,8 +1499,7 @@ msgstr "" "отсылать вашему респонденту." #: ../data/glade/preferences_window.glade.h:44 -msgid "" -"Gajim will automatically show new events by poping up the relative window" +msgid "Gajim will automatically show new events by poping up the relative window" msgstr "" "Gajim будет автоматически показывать новые события, поднимая соответствующее " "окно." @@ -1539,16 +1529,14 @@ msgstr "" "правом нижнем углу экрана" #: ../data/glade/preferences_window.glade.h:48 -msgid "" -"Gajim will only change the icon of the contact that triggered the new event" -msgstr "" -"Gajim будет лишь менять иконку у контакта, от которого пришло новое сообщение" +msgid "Gajim will only change the icon of the contact that triggered the new event" +msgstr "Gajim будет лишь менять иконку у контакта, от которого пришло новое сообщение" #: ../data/glade/preferences_window.glade.h:50 msgid "" "If checked, Gajim will also include information about the sender of the new " "emails" -msgstr "" +msgstr "Если отмечено, то Gajim будет включать информацию об отправителе новых писем." #: ../data/glade/preferences_window.glade.h:51 msgid "" @@ -1628,9 +1616,8 @@ msgid "One message _window:" msgstr "Одно _окно сообщения:" #: ../data/glade/preferences_window.glade.h:66 -#, fuzzy msgid "Outgoing Chat state noti_fications:" -msgstr "_Уведомление об изменении состояния:" +msgstr "Исходящие у_ведомления о состоянии беседы:" #: ../data/glade/preferences_window.glade.h:67 msgid "Play _sounds" @@ -1725,9 +1712,8 @@ msgstr "" "http://trac.gajim.org/wiki/GajimAndMusicPlayer" #: ../data/glade/preferences_window.glade.h:86 -#, fuzzy msgid "_Advanced Notifications Control..." -msgstr "Расширенный редактор настроек" +msgstr "_Дополнительные настройки уведомлений..." #: ../data/glade/preferences_window.glade.h:87 msgid "_After time:" @@ -1847,7 +1833,7 @@ msgstr "Разрешить" #: ../data/glade/privacy_list_window.glade.h:9 msgid "JabberID" -msgstr "Jabber ID:" +msgstr "JabberID" #: ../data/glade/privacy_list_window.glade.h:10 msgid "Order:" @@ -1872,6 +1858,10 @@ msgid "" "from\n" "to" msgstr "" +"нет\n" +"оба\n" +"от\n" +"к" #: ../data/glade/privacy_list_window.glade.h:18 msgid "to send me messages" @@ -2075,7 +2065,7 @@ msgstr "Выполнить команду..." #: ../data/glade/roster_contact_context_menu.glade.h:6 #: ../src/roster_window.py:1951 msgid "In_vite to" -msgstr "Пригласить в комнату" +msgstr "_Пригласить в" #: ../data/glade/roster_contact_context_menu.glade.h:7 #: ../data/glade/systray_context_menu.glade.h:2 @@ -2242,9 +2232,8 @@ msgid "_Send & Close" msgstr "Отправить и _закрыть" #: ../data/glade/subscription_request_window.glade.h:1 -#, fuzzy msgid "Au_thorize" -msgstr "_Авторизовать" +msgstr "А_вторизовать" #: ../data/glade/subscription_request_window.glade.h:2 msgid "Authorize contact so he can know when you're connected" @@ -2297,9 +2286,8 @@ msgid "Contact" msgstr "Контакт" #: ../data/glade/vcard_information_window.glade.h:12 -#, fuzzy msgid "Contact Information" -msgstr "Личная информация" +msgstr "Информация о контакте" #: ../data/glade/vcard_information_window.glade.h:24 #: ../data/glade/zeroconf_information_window.glade.h:4 @@ -2386,6 +2374,8 @@ msgid "" "setup you can select another one here.\n" "You might consider to change possible firewall settings." msgstr "" +"Если порт по умолчанию, который используется для входящих сообщения вас не устраивает, вы можете выбрать другой.\n" +"Так же возможно придется изменить некоторые настройки брандмауэра." #: ../data/glade/zeroconf_properties_window.glade.h:15 msgid "Modify Account" @@ -2432,7 +2422,7 @@ msgstr "Болгарский" #: ../src/chat_control.py:52 msgid "Briton" -msgstr "" +msgstr "Бриттский" #: ../src/chat_control.py:52 msgid "Czech" @@ -2471,7 +2461,6 @@ msgid "Italian" msgstr "Итальянский" #: ../src/chat_control.py:52 -#, fuzzy msgid "Norvegian b" msgstr "Норвежский b" @@ -2659,9 +2648,8 @@ msgid "Group Chat Message Received" msgstr "Получено сообщение в комнате" #: ../src/config.py:1086 -#, fuzzy msgid "GMail Email Received" -msgstr "Получено приглашение" +msgstr "Получено письмо на Gmail" #: ../src/config.py:1289 msgid "OpenPGP is not usable in this computer" @@ -2681,8 +2669,7 @@ msgstr "Непросмотренные события" #: ../src/config.py:1340 msgid "To change the account name, you must read all pending events." -msgstr "" -"Для смены имени учетной записи необходимо просмотреть ожидающие события." +msgstr "Для смены имени учетной записи необходимо просмотреть ожидающие события." #: ../src/config.py:1344 msgid "Account Name Already Used" @@ -2757,8 +2744,7 @@ msgstr "Вы не подключены к серверу" #: ../src/config.py:1601 msgid "Without a connection, you can not edit your personal information." -msgstr "" -"Чтобы отредактировать личную информацию, нужно подсоединиться к серверу." +msgstr "Чтобы отредактировать личную информацию, нужно подсоединиться к серверу." #: ../src/config.py:1605 msgid "Your server doesn't support Vcard" @@ -2973,8 +2959,7 @@ msgid "This bookmark has invalid data" msgstr "Эта закладка содержит неверные данные" #: ../src/config.py:2749 -msgid "" -"Please be sure to fill out server and room fields or remove this bookmark." +msgid "Please be sure to fill out server and room fields or remove this bookmark." msgstr "" "Пожалуйста, удостоверьтесь, что заполнены поля с именем сервера и комнаты, " "либо удалите эту закладку." @@ -3106,7 +3091,7 @@ msgstr "Имя контакта: %s" #: ../src/dialogs.py:61 #, python-format msgid "Jabber ID: %s" -msgstr "" +msgstr "Jabber ID: %s" #: ../src/dialogs.py:211 msgid "Group" @@ -3238,8 +3223,7 @@ msgstr "Не могу привязаться к порту %s." msgid "" "Maybe you have another running instance of Gajim. File Transfer will be " "canceled." -msgstr "" -"Возможно, запущен ещё один экземпляр Gajim. Передача файлов будет отменена." +msgstr "Возможно, запущен ещё один экземпляр Gajim. Передача файлов будет отменена." #: ../src/dialogs.py:1058 #, python-format @@ -3295,8 +3279,7 @@ msgstr "Начать беседу" msgid "" "Fill in the nickname or the Jabber ID of the contact you would like\n" "to send a chat message to:" -msgstr "" -"Введите ник или ID пользователя, которому вы хотите отправить сообщение:" +msgstr "Введите ник или ID пользователя, которому вы хотите отправить сообщение:" #. if offline or connecting #: ../src/dialogs.py:1276 ../src/dialogs.py:1635 ../src/dialogs.py:1764 @@ -3612,7 +3595,7 @@ msgstr "Описание" #. Id column #: ../src/disco.py:1490 msgid "Id" -msgstr "" +msgstr "ID" #: ../src/disco.py:1713 msgid "Subscribed" @@ -3806,7 +3789,7 @@ msgstr "Пауза" #: ../src/gajim.py:47 msgid "Gajim needs Xserver to run. Quiting..." -msgstr "Gajim для запуска требуется X-сервер. Выход..." +msgstr "Gajim'у для запуска требуется X-сервер. Выход..." #: ../src/gajim.py:51 msgid "Gajim needs PyGTK 2.6 or above" @@ -3838,8 +3821,7 @@ msgstr "" "стабильную версию с %s" #: ../src/gajim.py:64 -msgid "" -"Please make sure that GTK+ and PyGTK have libglade support in your system." +msgid "Please make sure that GTK+ and PyGTK have libglade support in your system." msgstr "Пожалуйста, удостоверьтесь что GTK+ и PyGTK поддерживают libglade." #: ../src/gajim.py:69 @@ -3855,7 +3837,7 @@ msgstr "Gajim для запуска требуется pywin32" msgid "" "Please make sure that Pywin32 is installed on your system. You can get it at " "%s" -msgstr "" +msgstr "Пожалуйста, удостоверьтесь что в вашей системе установлен Pywin32. Вы можете получить его на сайте: %s" #. set the icon to all newly opened wind #: ../src/gajim.py:238 @@ -3940,12 +3922,12 @@ msgid "New mail on %(gmail_mail_address)s" msgstr "Новое письмо на %(gmail_mail_address)s" #: ../src/gajim.py:1201 -#, fuzzy, python-format +#, python-format msgid "You have %d new mail conversation" msgid_plural "You have %d new mail conversations" -msgstr[0] "У вас есть %d непрочитанное сообщение" -msgstr[1] "У вас есть %d непрочитанных сообщения" -msgstr[2] "У вас есть %d непрочитанных сообщений" +msgstr[0] "У вас есть %d непрочитанное письмо" +msgstr[1] "У вас есть %d непрочитанных письма" +msgstr[2] "У вас есть %d непрочитанных письма" #. FIXME: emulate Gtalk client popups. find out what they parse and how #. they decide what to show @@ -3992,8 +3974,7 @@ msgstr "Такое имя уже есть" #: ../src/gajim.py:1559 msgid "Please type a new username for your local account" -msgstr "" -"Пожалуйста, введите новое имя пользователя для локальной учетной записи" +msgstr "Пожалуйста, введите новое имя пользователя для локальной учетной записи" #. it is good to notify the user #. in case he or she cannot see the output of the console @@ -4007,8 +3988,7 @@ msgstr "Network Manager не используется" #: ../src/gajim.py:2165 msgid "Session Management support not available (missing gnome.ui module)" -msgstr "" -"Отсутствует поддержка управления сессиями (отсутствует модуль gnome.ui)" +msgstr "Отсутствует поддержка управления сессиями (отсутствует модуль gnome.ui)" #: ../src/gajim-remote.py:66 msgid "Shows a help on specific command" @@ -4067,8 +4047,7 @@ msgstr "статус" #: ../src/gajim-remote.py:96 msgid "one of: offline, online, chat, away, xa, dnd, invisible " -msgstr "" -"один из: отключен, в сети, чат, отошел, недоступен, не беспокоить, невидимка" +msgstr "один из: отключен, в сети, чат, отошел, недоступен, не беспокоить, невидимка" #: ../src/gajim-remote.py:97 ../src/gajim-remote.py:118 #: ../src/gajim-remote.py:132 @@ -4124,13 +4103,11 @@ msgstr "PGP ключ" #: ../src/gajim-remote.py:119 ../src/gajim-remote.py:133 msgid "if specified, the message will be encrypted using this public key" -msgstr "" -"если указано, сообщение будет зашифровано с использованием открытого ключа" +msgstr "если указано, сообщение будет зашифровано с использованием открытого ключа" #: ../src/gajim-remote.py:121 ../src/gajim-remote.py:135 msgid "if specified, the message will be sent using this account" -msgstr "" -"если указано, сообщение будет отправлено с использованием этой учетной записи" +msgstr "если указано, сообщение будет отправлено с использованием этой учетной записи" #: ../src/gajim-remote.py:126 msgid "" @@ -4181,8 +4158,7 @@ msgstr "Путь до файла" #: ../src/gajim-remote.py:156 msgid "if specified, file will be sent using this account" -msgstr "" -"если указано, файл будет отправлен с использованием этой учетной записи" +msgstr "если указано, файл будет отправлен с использованием этой учетной записи" #: ../src/gajim-remote.py:161 msgid "Lists all preferences and their values" @@ -4198,8 +4174,7 @@ msgstr "ключ=значение" #: ../src/gajim-remote.py:167 msgid "'key' is the name of the preference, 'value' is the value to set it to" -msgstr "" -"'ключ' это название параметра, а 'значение' это то, что ему присваивают" +msgstr "'ключ' это название параметра, а 'значение' это то, что ему присваивают" #: ../src/gajim-remote.py:172 msgid "Deletes a preference item" @@ -4240,8 +4215,7 @@ msgstr "" "запись)" #: ../src/gajim-remote.py:207 -msgid "" -"Returns current status message(the global one unless account is specified)" +msgid "Returns current status message(the global one unless account is specified)" msgstr "" "Возвращает текущий статус (по умолчанию глобальный, если не указана учетная " "запись)" @@ -4468,10 +4442,8 @@ msgstr "" #: ../src/groupchat_control.py:1259 #, python-format -msgid "" -"Usage: /%s , opens a private chat window to the specified occupant." -msgstr "" -"Использование: /%s <ник>, открывает окно привата с указанным посетителем." +msgid "Usage: /%s , opens a private chat window to the specified occupant." +msgstr "Использование: /%s <ник>, открывает окно привата с указанным посетителем." #: ../src/groupchat_control.py:1263 #, python-format @@ -4483,9 +4455,7 @@ msgstr "Использование: /%s, очищает текстовое ок msgid "" "Usage: /%s [reason], closes the current window or tab, displaying reason if " "specified." -msgstr "" -"Использование: /%s [причина], закрывает текущее окно или вкладку, с приводя " -"причину, если она указана." +msgstr "Использование: /%s [причина], закрывает текущее окно или вкладку, приводя причину, если она указана." #: ../src/groupchat_control.py:1268 #, python-format @@ -4551,13 +4521,11 @@ msgstr "Использование: /%s, показывает список пр #: ../src/groupchat_control.py:1301 #, python-format msgid "Usage: /%s [topic], displays or updates the current group chat topic." -msgstr "" -"Использование: /%s [тема], показывает или изменяет текущую тему комнаты." +msgstr "Использование: /%s [тема], показывает или изменяет текущую тему комнаты." #: ../src/groupchat_control.py:1304 #, python-format -msgid "" -"Usage: /%s , sends a message without looking for other commands." +msgid "Usage: /%s , sends a message without looking for other commands." msgstr "" "Использование: /%s <сообщение>, отсылает сообщение без поиска других команд " "в нем." @@ -4573,8 +4541,7 @@ msgid "Are you sure you want to leave group chat \"%s\"?" msgstr "Вы точно хотите выйти из комнаты \"%s\"?" #: ../src/groupchat_control.py:1358 -msgid "" -"If you close this window, you will be disconnected from this group chat." +msgid "If you close this window, you will be disconnected from this group chat." msgstr "Если вы закроете это окно, то вы выйдете из этой комнаты." #: ../src/groupchat_control.py:1362 ../src/roster_window.py:3962 @@ -4716,8 +4683,7 @@ msgstr "Сообщение" msgid "" "Do you want to clean up the database? (STRONGLY NOT RECOMMENDED IF GAJIM IS " "RUNNING)" -msgstr "" -"Вы хотите очистить базу данных? (КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ ЕСЛИ GAJIM ЗАПУЩЕН)" +msgstr "Вы хотите очистить базу данных? (КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ ЕСЛИ GAJIM ЗАПУЩЕН)" #: ../src/history_manager.py:188 msgid "" @@ -4871,13 +4837,11 @@ msgstr "Публикация vCard не удалась" msgid "" "There was an error while publishing your personal information, try again " "later." -msgstr "" -"При публикации вашей личной информации произошла ошибка, попробуйте позже." +msgstr "При публикации вашей личной информации произошла ошибка, попробуйте позже." #: ../src/profile_window.py:374 msgid "Without a connection, you can not get your contact information." -msgstr "" -"Необходимо присоединиться к серверу для получения информации о контакте" +msgstr "Необходимо присоединиться к серверу для получения информации о контакте" #: ../src/roster_window.py:168 ../src/roster_window.py:223 msgid "Merged accounts" @@ -5018,7 +4982,7 @@ msgstr "_Новая комната" #: ../src/roster_window.py:1842 msgid "I would like to add you to my roster" -msgstr "Вы не против, если я добавлю Вас себе в ростер?" +msgstr "Вы не против, если я добавлю Вас к себе в ростер?" #: ../src/roster_window.py:2005 ../src/roster_window.py:2052 msgid "Send Group M_essage" @@ -5142,8 +5106,7 @@ msgstr "Неверная парольная фраза" #: ../src/roster_window.py:2725 msgid "Please retype your GPG passphrase or press Cancel." -msgstr "" -"Пожалуйста, введите парольную фразу для GPG еще раз или нажмите Отмена." +msgstr "Пожалуйста, введите парольную фразу для GPG еще раз или нажмите Отмена." #: ../src/roster_window.py:2782 ../src/roster_window.py:2842 msgid "You are participating in one or more group chats" @@ -5163,11 +5126,10 @@ msgstr "Нет доступной учетной записи" #: ../src/roster_window.py:2801 msgid "You must create an account before you can chat with other contacts." -msgstr "" -"Для начала беседы с другими людьми прежде необходимо создать учетную запись." +msgstr "Для начала беседы с другими людьми прежде необходимо создать учетную запись." #: ../src/roster_window.py:2899 -#, fuzzy, python-format +#, python-format msgid "\"%(title)s\" by %(artist)s" msgstr "%(artist)s \"%(title)s\"" @@ -5192,8 +5154,7 @@ msgstr "" "следующем подключении." #: ../src/roster_window.py:3956 -msgid "" -"You are about to create a metacontact. Are you sure you want to continue?" +msgid "You are about to create a metacontact. Are you sure you want to continue?" msgstr "Вы хотите создать метаконтакт?" #: ../src/roster_window.py:3958 @@ -5201,7 +5162,7 @@ msgid "" "Metacontacts are a way to regroup several contacts in one line. Generaly it " "is used when the same person has several Jabber accounts or transport " "accounts." -msgstr "" +msgstr "Метаконтакты это метод группировки нескольких контактов в одну запись. Обычно это используется когда один и тот же человек имеет несколько аккаунтов в Jabber или на транспортах." #: ../src/roster_window.py:4125 #, python-format @@ -5328,9 +5289,8 @@ msgid "since %s" msgstr "с %s" #: ../src/vcard.py:277 -#, fuzzy msgid "Affiliation:" -msgstr "Ранг: " +msgstr "Ранг:" #: ../src/vcard.py:285 msgid "" @@ -5413,10 +5373,8 @@ msgid "Not available as a result of being idle" msgstr "Автостатус 'Недоступен' из-за бездействия пользователя." #: ../src/common/config.py:82 -msgid "" -"List (space separated) of rows (accounts and groups) that are collapsed." -msgstr "" -"Список (через пробел) строк (учётных записей и групп), которые свёрнуты." +msgid "List (space separated) of rows (accounts and groups) that are collapsed." +msgstr "Список (через пробел) строк (учётных записей и групп), которые свёрнуты." #: ../src/common/config.py:87 msgid "Enable link-local/zeroconf messaging" @@ -5432,21 +5390,23 @@ msgid "" "'sometimes' - print time every print_ichat_every_foo_minutes minute.\n" "'never' - never print time." msgstr "" +"\"всегда\" - печатать время у каждого сообщения.\n" +"\"иногда\" - печатать время каждые несколько минут.\n" +"\"никогда\" - не печатать время." #: ../src/common/config.py:92 msgid "" "Print time in chats using Fuzzy Clock. Value of fuzziness from 1 to 4, or 0 " "to disable fuzzyclock. 1 is the most precise clock, 4 the less precise one. " "This is used only if print_time is 'sometimes'." -msgstr "" +msgstr "Печатать время в беседе как в неточных часах. Степень неточности может быть от 1 до 4, 0 отключает эту функцию. 1 самые точные часы, 4 самые неточные. Используется только если режим печати времени установлен на \"иногда\"." #: ../src/common/config.py:95 msgid "Treat * / _ pairs as possible formatting characters." msgstr "Обрабатывать пары * / _ как возможные форматирующие символы." #: ../src/common/config.py:96 -msgid "" -"If True, do not remove */_ . So *abc* will be bold but with * * not removed." +msgid "If True, do not remove */_ . So *abc* will be bold but with * * not removed." msgstr "" "Если True, то не будут удаляться */_. Так что *фыва* будет написано " "полужирным шрифтом, но * * удалены не будут" @@ -5461,13 +5421,13 @@ msgstr "" msgid "" "Character to add after nickname when using nick completion (tab) in group " "chat." -msgstr "" +msgstr "Символ, который нужно добавлять к нику, когда используется дополнение (tab) в комнате." #: ../src/common/config.py:109 msgid "" "Character to propose to add after desired nickname when desired nickname is " "used by someone else in group chat." -msgstr "" +msgstr "Символ, который будет добавляться после ника, когда желаемый ник уже занят кем-нибудь в комнате." #: ../src/common/config.py:142 msgid "Add * and [n] in roster title?" @@ -5521,21 +5481,20 @@ msgid "" msgstr "" #: ../src/common/config.py:155 -msgid "" -"Sent chat state notifications. Can be one of all, composing_only, disabled." -msgstr "" +msgid "Sent chat state notifications. Can be one of all, composing_only, disabled." +msgstr "Отсылать уведомления о состоянии беседы. Может быть одним из \"все\", \"только написание\", \"отключено\"." #: ../src/common/config.py:156 msgid "" "Displayed chat state notifications in chat windows. Can be one of all, " "composing_only, disabled." -msgstr "" +msgstr "Отображать состояния беседы в окне беседы. Может быть одним из \"все\", \"только написание\", \"отключено\"." #: ../src/common/config.py:158 msgid "" "When not printing time for every message (print_time==sometimes), print it " "every x minutes." -msgstr "" +msgstr "Есди отключена печать времени для каждого сообщения, то печатать его каждые X минут." #: ../src/common/config.py:159 msgid "Ask before closing a group chat tab/window." @@ -5571,26 +5530,23 @@ msgstr "IEC утверждает что KiB = 1024 байт, KB = 1000 байт" #: ../src/common/config.py:168 msgid "Notify of events in the system trayicon." -msgstr "" +msgstr "Уведомлять о событиях в системном лотке." #: ../src/common/config.py:174 msgid "Show tab when only one conversation?" msgstr "Показывать вкладку при одной беседе?" #: ../src/common/config.py:175 -#, fuzzy msgid "Show tabbed notebook border in chat windows?" -msgstr "Показывать границу вкладки при одной беседе?" +msgstr "Показывать границу вкладки в окне беседы?" #: ../src/common/config.py:176 msgid "Show close button in tab?" msgstr "Показывать кнопку закрытия на вкладке?" #: ../src/common/config.py:189 -msgid "" -"A semicolon-separated list of words that will be highlighted in group chats." -msgstr "" -"Список слов (через точку с запятой), которые будут подсвечиваться в комнатах." +msgid "A semicolon-separated list of words that will be highlighted in group chats." +msgstr "Список слов (через точку с запятой), которые будут подсвечиваться в комнатах." #: ../src/common/config.py:190 msgid "" @@ -5629,12 +5585,11 @@ msgstr "" "которого её не было в последний раз или она уже слишком старая." #: ../src/common/config.py:196 -#, fuzzy msgid "" "If False, Gajim will no longer print status line in chats when a contact " "changes his or her status and/or his or her status message." msgstr "" -"Если False, то вы больше не будете видеть статусную строку в окне беседы, " +"Если отключено, то Gajim больше не будет показывать статусную строку в окне беседы, " "когда контакт меняет его или её статус и/или сообщение о статусе." #: ../src/common/config.py:197 @@ -5644,28 +5599,27 @@ msgid "" "status and/or his or her status message. If \"all\" Gajim will print all " "status messages. If \"in_and_out\", gajim will only print FOO enters/leaves " "group chat." -msgstr "" +msgstr "может быть \"нет\", \"все\" или \"вход и выход\". Если установлено \"нет\", то Gajim больше не будет выводить сообщения о статусе в комнатах когда участник или участница меняет свой статус или сообщение о статусе. Если установлено в \"все\", то Gajim будет выводить все сообщения о статусе. Если установлено в \"вход и выход\", то Gajim будет выводить сообщение только когда кто-то входит или выходит из комнаты." #: ../src/common/config.py:199 msgid "Background color of contacts when they just signed in." -msgstr "" +msgstr "Фоновый цвет контакта, который только что вошел." #: ../src/common/config.py:200 msgid "Background color of contacts when they just signed out." -msgstr "" +msgstr "Фоновый цвет контакта, который только что вышел." #: ../src/common/config.py:202 -msgid "" -"If True, restored messages will use a smaller font than the default one." -msgstr "" +msgid "If True, restored messages will use a smaller font than the default one." +msgstr "Если установлено, то восстановленные сообщения будут иметь меньший шрифт." #: ../src/common/config.py:203 msgid "Don't show avatar for the transport itself." -msgstr "" +msgstr "Не показывать аватар для транспорта." #: ../src/common/config.py:204 msgid "Don't show roster in the system taskbar." -msgstr "" +msgstr "Не показывать ростер в панели задач." #: ../src/common/config.py:205 msgid "" @@ -5738,7 +5692,7 @@ msgstr "Скрывает список посетителей в окне ком msgid "" "In a chat, show the nickname at the beginning of a line only when it's not " "the same person talking than in previous message." -msgstr "" +msgstr "При беседе, показывать ник в начале строки только тогда, когда его произносит участник отличный от того, кто сказал предыдущее." #: ../src/common/config.py:219 msgid "Indentation when using merge consecutive nickame." @@ -5746,24 +5700,24 @@ msgstr "" #: ../src/common/config.py:220 msgid "List of colors that will be used to color nicknames in group chats." -msgstr "" +msgstr "Список цветов, которые будут использоваться для раскраски ников в комнатах." #: ../src/common/config.py:221 msgid "Ctrl-Tab go to next composing tab when none is unread." -msgstr "" +msgstr "Использовать Ctrl-Tab чтобы перейти к следующей вкладке с составляемым сообщением, когда нет непрочитанных сообщений." #: ../src/common/config.py:222 msgid "" "Should we show the confirm metacontacts creation dialog or not? Empty string " "means we never show the dialog." -msgstr "" +msgstr "А нам надо показывать диалог подтверждения о создании метаконтакта? Если тут пустая строка, то сообщение не будет показываться вообще." #: ../src/common/config.py:223 msgid "" "If True, you will be able to set a negative priority to your account in " "account modification window. BE CAREFULL, when you are logged in with a " "negative priority, you will NOT receive any message from your server." -msgstr "" +msgstr "Если установлено, то у вас появится возможность назначить для аккаунта приоритет меньше нуля в окне настройки аккаунта. БУДЬТЕ ОСТОРОЖНЫ, если вы подключитесь еще раз с отрицательным приоритетом, то вы НЕ получите никаких сообщений от сервера." #: ../src/common/config.py:234 msgid "" @@ -5794,17 +5748,17 @@ msgstr "Язык, который используется при проверк #: ../src/common/config.py:336 msgid "all or space separated status" -msgstr "" +msgstr "все или статусы, разделенные пробелом" #: ../src/common/config.py:337 msgid "'yes', 'no', or 'both'" -msgstr "" +msgstr "\"да\", \"нет\" или \"оба\"" #: ../src/common/config.py:338 ../src/common/config.py:340 #: ../src/common/config.py:341 ../src/common/config.py:344 #: ../src/common/config.py:345 msgid "'yes', 'no' or ''" -msgstr "" +msgstr "\"да\", \"нет\" или \"\"" #: ../src/common/config.py:351 msgid "Sleeping" @@ -5910,7 +5864,7 @@ msgstr "морской" #: ../src/common/connection_handlers.py:52 #: ../src/common/zeroconf/connection_handlers_zeroconf.py:44 msgid "Unable to load idle module" -msgstr "" +msgstr "Не могу загрузить модуль \"idle\"" #: ../src/common/connection_handlers.py:177 #: ../src/common/zeroconf/connection_handlers_zeroconf.py:233 @@ -5927,12 +5881,12 @@ msgstr "" #: ../src/common/connection_handlers.py:590 #, python-format msgid "Registration information for transport %s has not arrived in time" -msgstr "" +msgstr "Данные о регистрации для транспорта %s не пришли вовремя." #: ../src/common/connection_handlers.py:1523 -#, fuzzy, python-format +#, python-format msgid "Nickname not allowed: %s" -msgstr "Ник не обнаружен: %s" +msgstr "Ник недопустим: %s" #. password required to join #. we are banned @@ -5961,11 +5915,11 @@ msgstr "Такой комнаты нет." #: ../src/common/connection_handlers.py:1595 msgid "Group chat creation is restricted." -msgstr "" +msgstr "Создание комнат запрещено." #: ../src/common/connection_handlers.py:1598 msgid "Your registered nickname must be used." -msgstr "" +msgstr "Требуется использовать ваш зарегистрированный ник." #: ../src/common/connection_handlers.py:1601 msgid "You are not in the members list." @@ -5976,6 +5930,8 @@ msgid "" "Your desired nickname is in use or registered by another occupant.\n" "Please specify another nickname below:" msgstr "" +"Тот ник что вы ввели уже используется или зарегистрирован другим пользователем.\n" +"Пожалуйста укажите другой ник ниже:" #: ../src/common/connection_handlers.py:1659 msgid "I would like to add you to my roster." @@ -5985,7 +5941,7 @@ msgstr "Вы не возражаете, если я добавлю Вас в с #: ../src/common/connection_handlers.py:1680 #, python-format msgid "we are now subscribed to %s" -msgstr "" +msgstr "теперь мы подписались на %s" #: ../src/common/connection_handlers.py:1682 #, python-format @@ -5995,14 +5951,14 @@ msgstr "%s хочет отменить подписку" #: ../src/common/connection_handlers.py:1684 #, python-format msgid "we are now unsubscribed from %s" -msgstr "" +msgstr "теперь мы отписались от %s" #: ../src/common/connection_handlers.py:1854 #, python-format msgid "" "JID %s is not RFC compliant. It will not be added to your roster. Use roster " "management tools such as http://jru.jabberstudio.org/ to remove it" -msgstr "" +msgstr "JID %s не соответствует RFC. Он не будет добавлен в ваш ростер. Используйте средство управления ростера, например http://jru.jabberstudio.org/ чтобы удалить его" #: ../src/common/connection.py:175 #: ../src/common/zeroconf/connection_zeroconf.py:196 @@ -6012,12 +5968,12 @@ msgstr "Связь с учетной записью \"%s\" была потеря #: ../src/common/connection.py:176 msgid "Reconnect manually." -msgstr "" +msgstr "Переподключаться вручную." #: ../src/common/connection.py:187 ../src/common/connection.py:214 -#, fuzzy, python-format +#, python-format msgid "Transport %s answered wrongly to register request: %s" -msgstr "Транспорт %s неверно ответил на запрос о регистрации." +msgstr "Транспорт %s неверно ответил на запрос о регистрации: %s" #. wrong answer #: ../src/common/connection.py:213 @@ -6087,7 +6043,7 @@ msgstr "" #: ../src/common/connection.py:915 msgid "Not fetched because of invisible status" -msgstr "" +msgstr "Нельзя получить из-за невидимости" #. only say that to non Windows users #: ../src/common/dbus_support.py:33 @@ -6172,31 +6128,31 @@ msgstr "двенадцать" #. A "singular-form". It is used when talking about hour 0 #: ../src/common/fuzzyclock.py:47 ../src/common/fuzzyclock.py:55 msgid "%0 o'clock" -msgstr "" +msgstr "%0" #: ../src/common/fuzzyclock.py:47 ../src/common/fuzzyclock.py:55 msgid "five past %0" -msgstr "" +msgstr "пять минут %1" #: ../src/common/fuzzyclock.py:47 ../src/common/fuzzyclock.py:56 msgid "ten past %0" -msgstr "" +msgstr "десять минут %1" #: ../src/common/fuzzyclock.py:48 ../src/common/fuzzyclock.py:56 msgid "quarter past %0" -msgstr "" +msgstr "четверть %1" #: ../src/common/fuzzyclock.py:48 ../src/common/fuzzyclock.py:57 msgid "twenty past %0" -msgstr "" +msgstr "двадцать минут %1" #: ../src/common/fuzzyclock.py:49 ../src/common/fuzzyclock.py:57 msgid "twenty five past %0" -msgstr "" +msgstr "двадцать пять минут %1" #: ../src/common/fuzzyclock.py:49 ../src/common/fuzzyclock.py:58 msgid "half past %0" -msgstr "" +msgstr "половина %1" #: ../src/common/fuzzyclock.py:50 ../src/common/fuzzyclock.py:58 msgid "twenty five to %1" @@ -6220,7 +6176,7 @@ msgstr "без пяти %1" #: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60 msgid "%1 o'clock" -msgstr "" +msgstr "%1" #: ../src/common/fuzzyclock.py:62 msgid "Night" @@ -6239,7 +6195,6 @@ msgid "Almost noon" msgstr "День" #: ../src/common/fuzzyclock.py:63 -#, fuzzy msgid "Noon" msgstr "Полдень" @@ -6248,7 +6203,6 @@ msgid "Afternoon" msgstr "День" #: ../src/common/fuzzyclock.py:63 -#, fuzzy msgid "Evening" msgstr "Вечер" @@ -6523,9 +6477,8 @@ msgid "cyan" msgstr "синий" #: ../src/common/optparser.py:302 -#, fuzzy msgid "migrating logs database to indeces" -msgstr "создается БД истории" +msgstr "переносим журнал на индексы" #: ../src/common/passwords.py:86 #, python-format @@ -6533,17 +6486,15 @@ msgid "Gajim account %s" msgstr "Учётная запись Gajim %s" #: ../src/common/zeroconf/client_zeroconf.py:189 -msgid "" -"Connection to host could not be established: Incorrect answer from server." -msgstr "" +msgid "Connection to host could not be established: Incorrect answer from server." +msgstr "Нельзя подключиться к хосту: Неверный ответ от сервера." #: ../src/common/zeroconf/client_zeroconf.py:205 msgid "Connection to host could not be established" msgstr "Не удалось соединиться с хостом" #: ../src/common/zeroconf/client_zeroconf.py:334 -msgid "" -"Connection to host could not be established: Timeout while sending data." +msgid "Connection to host could not be established: Timeout while sending data." msgstr "Не удалось соединиться с хостом: истекло время ожидания." #: ../src/common/zeroconf/client_zeroconf.py:629 @@ -6567,23 +6518,22 @@ msgstr "" #: ../src/common/zeroconf/connection_zeroconf.py:218 msgid "Please check if Avahi is installed." -msgstr "" +msgstr "Пожалуйста, удостоверьтесь что Avahi установлен." #: ../src/common/zeroconf/connection_zeroconf.py:227 #: ../src/common/zeroconf/connection_zeroconf.py:231 -#, fuzzy msgid "Could not start local service" -msgstr "Не могу загрузить изображение" +msgstr "Не могу запустить локальный сервис" #: ../src/common/zeroconf/connection_zeroconf.py:228 #, python-format msgid "Unable to bind to port %d." -msgstr "" +msgstr "Не могу использовать порт %d." #: ../src/common/zeroconf/connection_zeroconf.py:232 #: ../src/common/zeroconf/connection_zeroconf.py:325 msgid "Please check if avahi-daemon is running." -msgstr "" +msgstr "Пожалуйста, удостоверьтесь что демон avahi запущен." #: ../src/common/zeroconf/connection_zeroconf.py:324 #, python-format @@ -6594,7 +6544,7 @@ msgstr "Не могу изменить статус учётной записи msgid "" "You are not connected or not visible to others. Your message could not be " "sent." -msgstr "" +msgstr "Вы не подключены или невидимы для других. Ваше сообщение нельзы отправить." #. we're not english #: ../src/common/zeroconf/connection_zeroconf.py:353 @@ -6606,8 +6556,3 @@ msgstr "[Это сообщение зашифровано]" msgid "Error while adding service. %s" msgstr "Ошибка при добавлении службы. %s" -#~ msgid "This is result of query." -#~ msgstr "Вот результат запроса." - -#~ msgid "Edit items on the list" -#~ msgstr "Редактировать элементы списка" diff --git a/src/adhoc_commands.py b/src/adhoc_commands.py index c3bdc0e7b..ecee22521 100644 --- a/src/adhoc_commands.py +++ b/src/adhoc_commands.py @@ -53,6 +53,7 @@ class CommandWindow: # retrieving widgets from xml self.xml = gtkgui_helpers.get_glade('adhoc_commands_window.glade') self.window = self.xml.get_widget('adhoc_commands_window') + self.window.connect('delete-event', self.on_adhoc_commands_window_delete_event) for name in ('back_button', 'forward_button', 'execute_button','close_button','stages_notebook', 'retrieving_commands_stage_vbox', @@ -101,7 +102,7 @@ class CommandWindow: self.remove_pulsing() def on_adhoc_commands_window_delete_event(self, *anything): - return self.stage_adhoc_commands_window_delete_event(self, *anything) + return self.stage_adhoc_commands_window_delete_event(self.window) def __del__(self): print "Object has been deleted." @@ -171,7 +172,7 @@ class CommandWindow: for (commandnode, commandname) in self.commandlist: radio = gtk.RadioButton(first_radio, label=commandname) radio.connect("toggled", self.on_command_radiobutton_toggled, commandnode) - if first_radio is None: + if not first_radio: first_radio = radio self.commandnode = commandnode self.command_list_vbox.pack_start(radio, expand=False) @@ -252,6 +253,7 @@ class CommandWindow: else: self.window.destroy() return False + return True def stage3_back_button_clicked(self, widget): self.stage3_submit_form('prev') @@ -264,10 +266,10 @@ class CommandWindow: def stage3_submit_form(self, action='execute'): self.data_form_widget.set_sensitive(False) - if self.data_form_widget.get_data_form() is None: - self.data_form_widget.hide() - else: + if self.data_form_widget.get_data_form(): self.data_form_widget.data_form.type='submit' + else: + self.data_form_widget.hide() self.close_button.set_sensitive(True) self.back_button.set_sensitive(False) @@ -284,13 +286,13 @@ class CommandWindow: self.remove_pulsing() self.sending_form_progressbar.hide() - if self.sessionid is None: + if not self.sessionid: self.sessionid = command.getAttr('sessionid') self.form_status = command.getAttr('status') self.commandnode = command.getAttr('node') - if command.getTag('x') is not None: + if command.getTag('x'): self.dataform = dataforms.ExtendForm(node=command.getTag('x')) self.data_form_widget.set_sensitive(True) @@ -307,18 +309,18 @@ class CommandWindow: else: self.data_form_widget.hide() - action = command.getTag('action') - if action is None: + actions = command.getTag('actions') + if actions: + # actions, actions, actions... + self.close_button.set_sensitive(True) + self.back_button.set_sensitive(actions.getTag('prev') is not None) + self.forward_button.set_sensitive(actions.getTag('next') is not None) + self.execute_button.set_sensitive(True) + else: self.close_button.set_sensitive(True) self.back_button.set_sensitive(False) self.forward_button.set_sensitive(False) self.execute_button.set_sensitive(True) - else: - # actions, actions, actions... - self.close_button.set_sensitive(True) - self.back_button.set_sensitive(action.getTag('prev') is not None) - self.forward_button.set_sensitive(action.getTag('next') is not None) - self.execute_button.set_sensitive(True) if self.form_status == 'completed': self.close_button.set_sensitive(True) @@ -329,7 +331,7 @@ class CommandWindow: self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked note = command.getTag('note') - if note is not None: + if note: self.notes_label.set_text(note.getData().decode('utf-8')) self.notes_label.set_no_show_all(False) self.notes_label.show() @@ -369,9 +371,9 @@ class CommandWindow: # close old stage self.stage_finish() - assert errorid is not None or error is not None + assert errorid or error - if errorid is not None: + if errorid: # we've got error code, display appropriate message try: errorname = xmpp.NS_STANZAS + ' ' + str(errorid) @@ -380,7 +382,7 @@ class CommandWindow: del errorname, errordesc except KeyError: # when stanza doesn't have error description error = 'Service returned an error.' - elif error is not None: + elif error: # we've got error message pass else: @@ -409,7 +411,7 @@ class CommandWindow: def setup_pulsing(self, progressbar): '''Set the progressbar to pulse. Makes a custom function to repeatedly call progressbar.pulse() method.''' - assert self.pulse_id is None + assert not self.pulse_id assert isinstance(progressbar, gtk.ProgressBar) def callback(): @@ -421,7 +423,7 @@ class CommandWindow: def remove_pulsing(self): '''Stop pulsing, useful when especially when removing widget.''' - if self.pulse_id is not None: + if self.pulse_id: gobject.source_remove(self.pulse_id) self.pulse_id=None @@ -436,7 +438,7 @@ class CommandWindow: # FIXME: move to connection_handlers.py # is error => error stage error = response.getError() - if error is not None: + if error: # extracting error description from xmpp/protocol.py self.stage5(errorid = error) return @@ -470,10 +472,10 @@ class CommandWindow: 'action':action }) - if self.sessionid is not None: + if self.sessionid: cmdnode.setAttr('sessionid', self.sessionid) - if self.data_form_widget.data_form is not None: + if self.data_form_widget.data_form: # cmdnode.addChild(node=dataforms.DataForm(tofill=self.data_form_widget.data_form)) # FIXME: simplified form to send @@ -482,7 +484,7 @@ class CommandWindow: def callback(response): # FIXME: move to connection_handlers.py err = response.getError() - if err is not None: + if err: self.stage5(errorid = err) else: self.stage3_next_form(response.getTag('command')) @@ -491,8 +493,8 @@ class CommandWindow: def send_cancel(self): '''Send the command with action='cancel'. ''' - assert self.commandnode is not None - if self.sessionid is not None and self.account.connection: + assert self.commandnode + if self.sessionid and self.account.connection: # we already have sessionid, so the service sent at least one reply. stanza = xmpp.Iq(typ='set', to=self.jid) stanza.addChild('command', attrs={ diff --git a/src/chat_control.py b/src/chat_control.py index baa9beec8..9bf90ced9 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -4,6 +4,8 @@ ## Copyright (C) 2006-2007 Nikos Kouremenos ## Copyright (C) 2006 Travis Shirk ## Copyright (C) 2006 Dimitur Kirov +## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -129,13 +131,39 @@ class ChatControlBase(MessageControl): id = self.widget.connect('key_press_event', self._on_keypress_event) self.handlers[id] = self.widget + # Create banner and connect signals widget = self.xml.get_widget('banner_eventbox') + widget.set_property('height-request', gajim.config.get('chat_avatar_height')) id = widget.connect('button-press-event', self._on_banner_eventbox_button_press_event) self.handlers[id] = widget + # Init DND + self.TARGET_TYPE_URI_LIST = 80 + self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ), + ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP, 0)] + id = self.widget.connect('drag_data_received', + self._on_drag_data_received) + self.handlers[id] = self.widget + self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION | + gtk.DEST_DEFAULT_HIGHLIGHT | + gtk.DEST_DEFAULT_DROP, + self.dnd_list, gtk.gdk.ACTION_COPY) # Create textviews and connect signals self.conv_textview = ConversationTextview(self.account) + # FIXME: DND on non editable TextView, find a better way + self.drag_entered = False + id = self.conv_textview.tv.connect('drag_data_received', + self._on_drag_data_received) + self.handlers[id] = self.conv_textview.tv + id = self.conv_textview.tv.connect('drag_motion', self._on_drag_motion) + self.handlers[id] = self.conv_textview.tv + id = self.conv_textview.tv.connect('drag_leave', self._on_drag_leave) + self.handlers[id] = self.conv_textview.tv + self.conv_textview.tv.drag_dest_set(gtk.DEST_DEFAULT_MOTION | + gtk.DEST_DEFAULT_HIGHLIGHT | + gtk.DEST_DEFAULT_DROP, + self.dnd_list, gtk.gdk.ACTION_COPY) self.conv_scrolledwindow = self.xml.get_widget( 'conversation_scrolledwindow') @@ -149,6 +177,7 @@ class ChatControlBase(MessageControl): self.handlers[id] = widget self.scroll_to_end_id = None self.was_at_the_end = True + # add MessageTextView to UI and connect signals self.msg_scrolledwindow = self.xml.get_widget('message_scrolledwindow') self.msg_textview = MessageTextView() @@ -164,6 +193,13 @@ class ChatControlBase(MessageControl): id = self.msg_textview.connect('populate_popup', self.on_msg_textview_populate_popup) self.handlers[id] = self.msg_textview + # Setup DND + id = self.msg_textview.connect('drag_data_received', + self._on_drag_data_received) + self.handlers[id] = self.msg_textview + self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION | + gtk.DEST_DEFAULT_HIGHLIGHT, + self.dnd_list, gtk.gdk.ACTION_COPY) self.update_font() @@ -481,6 +517,22 @@ class ChatControlBase(MessageControl): self.handle_message_textview_mykey_press(widget, event_keyval, event_keymod) + def _on_drag_data_received(self, widget, context, x, y, selection, + target_type, timestamp): + pass # Derived classes SHOULD implement this method + + def _on_drag_leave(self, widget, context, time): + # FIXME: DND on non editable TextView, find a better way + self.drag_entered = False + self.conv_textview.tv.set_editable(False) + + def _on_drag_motion(self, widget, context, x, y, time): + # FIXME: DND on non editable TextView, find a better way + if not self.drag_entered: + # We drag new data over the TextView, make it editable to catch dnd + self.drag_entered_conv = True + self.conv_textview.tv.set_editable(True) + def _process_command(self, message): if not message or message[0] != '/': return False @@ -496,23 +548,23 @@ class ChatControlBase(MessageControl): self.clear(self.msg_textview) # clear message textview too return True elif message == 'compact' and not len(message_array): - self.chat_buttons_set_visible(not self.hide_chat_buttons_current) + self.chat_buttons_set_visible(not self.hide_chat_buttons) self.clear(self.msg_textview) return True return False def send_message(self, message, keyID = '', type = 'chat', chatstate = None, - msg_id = None, composing_jep = None, resource = None): + msg_id = None, composing_xep = None, resource = None, + process_command = True): '''Send the given message to the active tab. Doesn't return None if error ''' if not message or message == '\n': return 1 - - if not self._process_command(message): + if not process_command or not self._process_command(message): ret = MessageControl.send_message(self, message, keyID, type = type, chatstate = chatstate, msg_id = msg_id, - composing_jep = composing_jep, resource = resource, + composing_xep = composing_xep, resource = resource, user_nick = self.user_nick) if ret: return ret @@ -569,7 +621,6 @@ class ChatControlBase(MessageControl): full_jid != self.parent_win.get_active_jid() or \ not self.parent_win.is_active() or not end)) or \ (gc_message and \ - gajim.interface.minimized_controls.has_key(self.account) and \ jid in gajim.interface.minimized_controls[self.account])) and \ kind in ('incoming', 'incoming_queue'): # we want to have save this message in events list @@ -667,31 +718,21 @@ class ChatControlBase(MessageControl): gajim.interface.instances['logs'][jid] = \ history_window.HistoryWindow(jid, self.account) - def _on_compact_view_menuitem_activate(self, widget): - isactive = widget.get_active() - self.chat_buttons_set_visible(isactive) - - def _on_minimize_menuitem_activate(self, widget): + def on_minimize_menuitem_toggled(self, widget): '''When a grouchat is minimized, unparent the tab, put it in roster etc''' - win = gajim.interface.msg_win_mgr.get_window(self.contact.jid, self.account) - ctrl = win.get_control(self.contact.jid, self.account) - - ctrl_page = win.notebook.page_num(ctrl.widget) - control = win.notebook.get_nth_page(ctrl_page) - - win.notebook.remove_page(ctrl_page) - control.unparent() - ctrl.parent_win = None - - if not gajim.interface.minimized_controls.has_key(self.account): - gajim.interface.minimized_controls[self.account] = {} - gajim.interface.minimized_controls[self.account][self.contact.jid] = ctrl - - del win._controls[self.account][self.contact.jid] - - win.check_tabs() - gajim.interface.roster.add_groupchat_to_roster(self.account, - self.contact.jid, status = self.subject) + old_value = False + minimized_gc = gajim.config.get_per('accounts', self.account, + 'minimized_gc').split() + if self.contact.jid in minimized_gc: + old_value = True + minimize = widget.get_active() + if minimize and not self.contact.jid in minimized_gc: + minimized_gc.append(self.contact.jid) + if not minimize and self.contact.jid in minimized_gc: + minimized_gc.remove(self.contact.jid) + if old_value != minimize: + gajim.config.set_per('accounts', self.account, 'minimized_gc', + ' '.join(minimized_gc)) def set_control_active(self, state): if state: @@ -825,11 +866,14 @@ class ChatControlBase(MessageControl): room_jid, nick = gajim.get_room_and_nick_from_fjid(jid) groupchat_control = gajim.interface.msg_win_mgr.get_control( room_jid, self.account) - if not groupchat_control and \ - gajim.interface.minimized_controls.has_key(self.account) and \ - room_jid in gajim.interface.minimized_controls[self.account]: + if room_jid in gajim.interface.minimized_controls[self.account]: groupchat_control = \ gajim.interface.minimized_controls[self.account][room_jid] + contact = \ + gajim.contacts.get_contact_with_highest_priority(self.account, \ + room_jid) + if contact: + gajim.interface.roster.draw_contact(room_jid, self.account) groupchat_control.draw_contact(nick) mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account) if mw: @@ -903,7 +947,7 @@ class ChatControl(ChatControlBase): '''A control for standard 1-1 chat''' TYPE_ID = message_control.TYPE_CHAT old_msg_kind = None # last kind of the printed message - CHAT_CMDS = ['clear', 'compact', 'help', 'ping'] + CHAT_CMDS = ['clear', 'compact', 'help', 'me', 'ping', 'say'] def __init__(self, parent_win, contact, acct, resource = None): ChatControlBase.__init__(self, self.TYPE_ID, parent_win, @@ -915,21 +959,10 @@ class ChatControl(ChatControlBase): id = widget.connect('clicked', self.on_actions_button_clicked) self.handlers[id] = widget - hide_chat_buttons_always = gajim.config.get( - 'always_hide_chat_buttons') - self.chat_buttons_set_visible(hide_chat_buttons_always) + compact_view = gajim.config.get('compact_view') + self.chat_buttons_set_visible(compact_view) self.widget_set_visible(self.xml.get_widget('banner_eventbox'), gajim.config.get('hide_chat_banner')) - # Initialize drag-n-drop - self.TARGET_TYPE_URI_LIST = 80 - self.dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ) ] - id = self.widget.connect('drag_data_received', - self._on_drag_data_received) - self.handlers[id] = self.widget - self.widget.drag_dest_set(gtk.DEST_DEFAULT_MOTION | - gtk.DEST_DEFAULT_HIGHLIGHT | - gtk.DEST_DEFAULT_DROP, - self.dnd_list, gtk.gdk.ACTION_COPY) # keep timeout id and window obj for possible big avatar # it is on enter-notify and leave-notify so no need to be per jid @@ -1085,6 +1118,8 @@ class ChatControl(ChatControlBase): jid = contact.jid banner_name_label = self.xml.get_widget('banner_name_label') + banner_eventbox = self.xml.get_widget('banner_eventbox') + name = contact.get_shown_name() if self.resource: name += '/' + self.resource @@ -1111,8 +1146,10 @@ class ChatControl(ChatControlBase): status = contact.status if status is not None: + self.status_tooltip.set_tip(banner_eventbox, status) + self.status_tooltip.enable() banner_name_label.set_ellipsize(pango.ELLIPSIZE_END) - status = helpers.reduce_chars_newlines(status, max_lines = 2) + status = helpers.reduce_chars_newlines(status, max_lines = 1) status_escaped = gobject.markup_escape_text(status) font_attrs, font_attrs_small = self.get_font_attrs() @@ -1121,12 +1158,12 @@ class ChatControl(ChatControlBase): if cs and st in ('composing_only', 'all'): if contact.show == 'offline': chatstate = '' - elif contact.composing_jep == 'JEP-0085': + elif contact.composing_xep == 'XEP-0085': if st == 'all' or cs == 'composing': chatstate = helpers.get_uf_chatstate(cs) else: chatstate = '' - elif contact.composing_jep == 'JEP-0022': + elif contact.composing_xep == 'XEP-0022': if cs in ('composing', 'paused'): # only print composing, paused chatstate = helpers.get_uf_chatstate(cs) @@ -1145,9 +1182,6 @@ class ChatControl(ChatControlBase): if status_escaped: label_text += '\n%s' %\ (font_attrs_small, status_escaped) - banner_eventbox = self.xml.get_widget('banner_eventbox') - self.status_tooltip.set_tip(banner_eventbox, status) - self.status_tooltip.enable() else: self.status_tooltip.disable() # setup the label that holds name and jid @@ -1196,6 +1230,9 @@ class ChatControl(ChatControlBase): if message_array == ['']: message_array = [] + if command == 'me': + return False # This is not really a command + if command == 'help': if len(message_array): subcommand = message_array.pop(0) @@ -1204,10 +1241,19 @@ class ChatControl(ChatControlBase): self.get_command_help(command) self.clear(self.msg_textview) return True - elif command == 'ping' and not len(message_array): - gajim.connections[self.account].sendPing(self.contact) + elif command == 'ping': + if not len(message_array): + gajim.connections[self.account].sendPing(self.contact) + else: + self.get_command_help(command) self.clear(self.msg_textview) return True + elif command == 'say': + return False + else: + self.print_conversation(_('No such command: /%s (if you want to send ' + 'this, prefix it with /say)') % command, 'info') + return True return False def get_command_help(self, command): @@ -1215,13 +1261,21 @@ class ChatControl(ChatControlBase): self.print_conversation(_('Commands: %s') % ChatControl.CHAT_CMDS, 'info') elif command == 'clear': - self.print_conversation(_('Usage: /%s, clears the text window.'), - 'info') + self.print_conversation(_('Usage: /%s, clears the text window.') % \ + command, 'info') elif command == 'compact': - self.print_conversation(_('Usage: /%s, hide the chat buttons.'), - 'info') + self.print_conversation(_('Usage: /%s, hide the chat buttons.') % \ + command, 'info') + elif command == 'me': + self.print_conversation(_('Usage: /%s , sends action to the ' + 'current group chat. Use third person. (e.g. /%s explodes.)') % \ + (command, command), 'info') elif command == 'ping': - self.print_conversation(_(''), 'info') + self.print_conversation(_('Usage: /%s, sends a ping to the contact') %\ + command, 'info') + elif command == 'say': + self.print_conversation(_('Usage: /%s, send the message to the contact') %\ + command, 'info') else: self.print_conversation(_('No help info for /%s') % command, 'info') @@ -1230,6 +1284,12 @@ class ChatControl(ChatControlBase): if message in ('', None, '\n') or self._process_command(message): return + # Do we need to process command for the message ? + process_command = True + if message.startswith('/say'): + message = message[5:] + process_command = False + # refresh timers self.reset_kbd_mouse_timeout_vars() @@ -1244,10 +1304,10 @@ class ChatControl(ChatControlBase): chatstates_on = gajim.config.get('outgoing_chat_state_notifications') != \ 'disabled' - composing_jep = contact.composing_jep + composing_xep = contact.composing_xep chatstate_to_send = None if chatstates_on and contact is not None: - if composing_jep is None: + if composing_xep is None: # no info about peer # send active to discover chat state capabilities # this is here (and not in send_chatstate) @@ -1256,13 +1316,13 @@ class ChatControl(ChatControlBase): if contact.our_chatstate: # We already asked for xep 85, don't ask it twice - composing_jep = 'asked_once' + composing_xep = 'asked_once' chatstate_to_send = 'active' contact.our_chatstate = 'ask' # pseudo state # if peer supports jep85 and we are not 'ask', send 'active' # NOTE: first active and 'ask' is set in gajim.py - elif composing_jep is not False: + elif composing_xep is not False: #send active chatstate on every message (as JEP says) chatstate_to_send = 'active' contact.our_chatstate = 'active' @@ -1272,7 +1332,8 @@ class ChatControl(ChatControlBase): self._schedule_activity_timers() if not ChatControlBase.send_message(self, message, keyID, type = 'chat', - chatstate = chatstate_to_send, composing_jep = composing_jep): + chatstate = chatstate_to_send, composing_xep = composing_xep, + process_command = process_command): self.print_conversation(message, self.contact.jid, encrypted = encrypted) @@ -1441,13 +1502,13 @@ class ChatControl(ChatControlBase): jid = self.contact.jid num_unread = len(gajim.events.get_events(self.account, jid, ['printed_' + self.type_id, self.type_id])) - # Set tab image (always 16x16); unread messages show the 'message' image + # Set tab image (always 16x16); unread messages show the 'event' image tab_img = None if num_unread and gajim.config.get('show_unread_tab_icon'): img_16 = gajim.interface.roster.get_appropriate_state_images( - self.contact.jid, icon_name = 'message') - tab_img = img_16['message'] + self.contact.jid, icon_name = 'event') + tab_img = img_16['event'] else: contact = gajim.contacts.get_contact_with_highest_priority( self.account, self.contact.jid) @@ -1474,7 +1535,6 @@ class ChatControl(ChatControlBase): toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem') add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem') send_file_menuitem = xml.get_widget('send_file_menuitem') - compact_view_menuitem = xml.get_widget('compact_view_menuitem') information_menuitem = xml.get_widget('information_menuitem') contact = self.parent_win.get_active_contact() @@ -1493,14 +1553,14 @@ class ChatControl(ChatControlBase): # If we don't have resource, we can't do file transfer # in transports, contact holds our info we need to disable it too - if contact.resource and contact.jid.find('@') != -1: + if self.TYPE_ID == message_control.TYPE_PM and self.gc_contact.jid and \ + self.gc_contact.resource: + send_file_menuitem.set_sensitive(True) + elif contact.resource and contact.jid.find('@') != -1: send_file_menuitem.set_sensitive(True) else: send_file_menuitem.set_sensitive(False) - # compact_view_menuitem - compact_view_menuitem.set_active(self.hide_chat_buttons_current) - # add_to_roster_menuitem if _('Not in Roster') in contact.groups: add_to_roster_menuitem.show() @@ -1517,9 +1577,6 @@ class ChatControl(ChatControlBase): id = send_file_menuitem.connect('activate', self._on_send_file_menuitem_activate) self.handlers[id] = send_file_menuitem - id = compact_view_menuitem.connect('activate', - self._on_compact_view_menuitem_activate) - self.handlers[id] = compact_view_menuitem id = add_to_roster_menuitem.connect('activate', self._on_add_to_roster_menuitem_activate) self.handlers[id] = add_to_roster_menuitem @@ -1565,7 +1622,7 @@ class ChatControl(ChatControlBase): if contact.show == 'offline': return - if contact.composing_jep is False: # jid cannot do jep85 nor jep22 + if contact.composing_xep is False: # jid cannot do xep85 nor xep22 return # if the new state we wanna send (state) equals @@ -1573,7 +1630,7 @@ class ChatControl(ChatControlBase): if contact.our_chatstate == state: return - if contact.composing_jep is None: + if contact.composing_xep is None: # we don't know anything about jid, so return # NOTE: # send 'active', set current state to 'ask' and return is done @@ -1587,7 +1644,7 @@ class ChatControl(ChatControlBase): # in JEP22, when we already sent stop composing # notification on paused, don't resend it - if contact.composing_jep == 'JEP-0022' and \ + if contact.composing_xep == 'XEP-0022' and \ contact.our_chatstate in ('paused', 'active', 'inactive') and \ state is not 'composing': # not composing == in (active, inactive, gone) contact.our_chatstate = 'active' @@ -1609,7 +1666,7 @@ class ChatControl(ChatControlBase): self.reset_kbd_mouse_timeout_vars() MessageControl.send_message(self, None, chatstate = state, - msg_id = contact.msg_id, composing_jep = contact.composing_jep) + msg_id = contact.msg_id, composing_xep = contact.composing_xep) contact.our_chatstate = state if contact.our_chatstate == 'active': self.reset_kbd_mouse_timeout_vars() @@ -1638,20 +1695,19 @@ class ChatControl(ChatControlBase): del self.handlers[i] self.conv_textview.del_handlers() self.msg_textview.destroy() - def allow_shutdown(self, method): if time.time() - gajim.last_message_time[self.account]\ [self.get_full_jid()] < 2: # 2 seconds dialog = dialogs.ConfirmationDialog( - #%s is being replaced in the code with JID + # %s is being replaced in the code with JID _('You just received a new message from "%s"') % self.contact.jid, _('If you close this tab and you have history disabled, '\ 'this message will be lost.')) if dialog.get_response() != gtk.RESPONSE_OK: - return False #stop the propagation of the event - return True + return 'no' # stop the propagation of the event + return 'yes' def handle_incoming_chatstate(self): ''' handle incoming chatstate that jid SENT TO us ''' @@ -1710,17 +1766,23 @@ class ChatControl(ChatControlBase): def _on_drag_data_received(self, widget, context, x, y, selection, target_type, timestamp): - # If not resource, we can't send file - if not self.contact.resource: + # If no resource is known, we can't send a file + if self.TYPE_ID == message_control.TYPE_PM: + c = self.gc_contact + else: + c = self.contact + if not c.resource: return if target_type == self.TARGET_TYPE_URI_LIST: + if not selection.data: + return uri = selection.data.strip() uri_splitted = uri.split() # we may have more than one file dropped for uri in uri_splitted: path = helpers.get_file_path_from_dnd_dropped_uri(uri) if os.path.isfile(path): # is it file? ft = gajim.interface.instances['file_transfers'] - ft.send_file(self.account, self.contact, path) + ft.send_file(self.account, c, path) def _on_message_tv_buffer_changed(self, textbuffer): self.kbd_activity_in_last_5_secs = True @@ -1833,7 +1895,7 @@ class ChatControl(ChatControlBase): show_transports = gajim.config.get('show_transports_group') if (not show_transports and gajim.jid_is_transport(jid)) or \ (not show_offline and typ == 'chat' and \ - len(gajim.contacts.get_contact(self.account, jid)) < 2): + len(gajim.contacts.get_contacts(self.account, jid)) < 2): gajim.interface.roster.really_remove_contact(self.contact, self.account) elif typ == 'pm': @@ -1919,8 +1981,12 @@ class ChatControl(ChatControlBase): self.bigger_avatar_window.window.set_cursor(cursor) def _on_send_file_menuitem_activate(self, widget): + if self.TYPE_ID == message_control.TYPE_PM: + c = self.gc_contact + else: + c = self.contact gajim.interface.instances['file_transfers'].show_file_send_request( - self.account, self.contact) + self.account, c) def _on_add_to_roster_menuitem_activate(self, widget): dialogs.AddNewContactWindow(self.account, self.contact.jid) diff --git a/src/common/atom.py b/src/common/atom.py index 7490f3bc7..86c75c5ba 100644 --- a/src/common/atom.py +++ b/src/common/atom.py @@ -63,10 +63,11 @@ class OldEntry(xmpp.Node, object): else: main_feed = None - if self.getTag('source-feed') is not None: - source_feed = self.getTag('source-feed').getTagData('title') + if self.getTag('feed') is not None: + source_feed = self.getTag('feed').getTagData('title') else: source_feed = None + if main_feed is not None and source_feed is not None: return u'%s: %s' % (main_feed, source_feed) @@ -78,14 +79,13 @@ class OldEntry(xmpp.Node, object): return u'' feed_title = property(get_feed_title, None, None, - ''' Title of feed. It is built from entry's original feed title and title of feed + ''' Title of feed. It is built from entry''s original feed title and title of feed which delivered this entry. ''') def get_feed_link(self): - ''' Get a link to main page of feed (in pubsub.com: second link of rel='alternate', - first contains raw xml data). ''' + ''' Get source link ''' try: - return self.getTag('source-feed').getTags('link', {'rel':'alternate'})[1].getData() + return self.getTag('feed').getTags('link',{'rel':'alternate'})[1].getData() except: return None diff --git a/src/common/caps.py b/src/common/caps.py new file mode 100644 index 000000000..49179db64 --- /dev/null +++ b/src/common/caps.py @@ -0,0 +1,259 @@ +## +## Copyright (C) 2006 Gajim Team +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 2 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from itertools import * +import xmpp +import xmpp.features_nb +import gajim + +class CapsCache(object): + ''' This object keeps the mapping between caps data and real disco + features they represent, and provides simple way to query that info. + It is application-wide, that is there's one object for all + connections. + Goals: + * handle storing/retrieving info from database + * cache info in memory + * expose simple interface + Properties: + * one object for all connections (move to logger.py?) + * store info efficiently (a set() of urls -- we can assume there won't be + too much of these, ensure that (X,Y,Z1) and (X,Y,Z2) has different + features. + + Connections with other objects: (TODO) + + Interface: + + # object creation + >>> cc=CapsCache(logger_object) + + >>> caps=('http://exodus.jabberstudio.org/caps', '0.9', None) # node, ver, ext + >>> muc='http://jabber.org/protocol/muc' + >>> chatstates='http://jabber.org/protocol/chatstates' + + # retrieving data + >>> muc in cc[caps].features + True + >>> muc in cc[caps] + True + >>> chatstates in cc[caps] + False + >>> cc[caps].identities + set({'category':'client', 'type':'pc'}) + >>> x=cc[caps] # more efficient if making several queries for one set of caps + ATypicalBlackBoxObject + >>> muc in x + True + >>> x.node + 'http://exodus.jabberstudio.org/caps' + + # retrieving data (multiple exts case) + >>> caps=('http://gajim.org/caps', '0.9', ('csn', 'ft')) + >>> muc in cc[caps] + True + + # setting data + >>> newcaps=('http://exodus.jabberstudio.org/caps', '0.9a', None) + >>> cc[newcaps].identities.add({'category':'client', 'type':'pc', 'name':'Gajim'}) + >>> cc[newcaps].features+=muc # same as: + >>> cc[newcaps]+=muc + >>> cc[newcaps]['csn']+=chatstates # adding data as if ext was 'csn' + # warning: no feature removal! + ''' + def __init__(self, logger=None): + ''' Create a cache for entity capabilities. ''' + # our containers: + # __names is a string cache; every string long enough is given + # another object, and we will have plenty of identical long + # strings. therefore we can cache them + # TODO: maybe put all known xmpp namespace strings here + # (strings given in xmpppy)? + # __cache is a dictionary mapping: pair of node and version maps + # to CapsCacheItem object + # __CacheItem is a class that stores data about particular + # client (node/version pair) + self.__names = {} + self.__cache = {} + + class CacheItem(object): + ''' TODO: logging data into db ''' + def __init__(ciself, node, version, ext=None): + # cached into db + ciself.node = node + ciself.version = version + ciself.features = set() + ciself.ext = ext + ciself.exts = {} + + # set of tuples: (category, type, name) + # (dictionaries are not hashable, so cannot be in sets) + ciself.identities = set() + + # not cached into db: + # have we sent the query? + # 0 == not queried + # 1 == queried + # 2 == got the answer + ciself.queried = 0 + + class CacheQuery(object): + def __init__(cqself, proxied): + cqself.proxied=proxied + + def __getattr__(cqself, obj): + if obj!='exts': return getattr(cqself.proxied[0], obj) + return set(chain(ci.features for ci in cqself.proxied)) + + def __getitem__(ciself, exts): + if not exts: # (), [], None, False, whatever + return ciself + if isinstance(exts, basestring): + exts=(exts,) + if len(exts)==1: + ext=exts[0] + if ext in ciself.exts: + return ciself.exts[ext] + x=CacheItem(ciself.node, ciself.version, ext) + ciself.exts[ext]=x + return x + proxied = [ciself] + proxied.extend(ciself[(e,)] for e in exts) + return ciself.CacheQuery(proxied) + + def update(ciself, identities, features): + # NOTE: self refers to CapsCache object, not to CacheItem + self.identities=identities + self.features=features + self.logger.add_caps_entry( + ciself.node, ciself.version, ciself.ext, + identities, features) + + self.__CacheItem = CacheItem + + # prepopulate data which we are sure of; note: we do not log these info + gajimnode = 'http://gajim.org/caps' + + gajimcaps=self[(gajimnode, '0.11.1')] + gajimcaps.category='client' + gajimcaps.type='pc' + gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI, + xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_COMMANDS, + xmpp.NS_DISCO_INFO, xmpp.NS_PING, xmpp.NS_TIME_REVISED)) + gajimcaps['cstates'].features=set((xmpp.NS_CHATSTATES,)) + gajimcaps['xhtml'].features=set((xmpp.NS_XHTML_IM,)) + + # TODO: older gajim versions + + # start logging data from the net + self.logger = logger + + def load_from_db(self): + # get data from logger... + if self.logger is not None: + for node, ver, ext, identities, features in self.logger.iter_caps_data(): + x=self[(node, ver, ext)] + x.identities=identities + x.features=features + x.queried=2 + + def __getitem__(self, caps): + node_version = caps[:2] + if node_version in self.__cache: + return self.__cache[node_version][caps[2]] + node, version = self.__names.setdefault(caps[0], caps[0]), caps[1] + x=self.__CacheItem(node, version) + self.__cache[(node, version)]=x + return x + + def preload(self, account, jid, node, ver, exts): + ''' Preload data about (node, ver, exts) caps using disco + query to jid using proper connection. Don't query if + the data is already in cache. ''' + q=self[(node, ver, ())] + qq=q + + if q.queried==0: + # do query for bare node+version pair + # this will create proper object + q.queried=1 + account.discoverInfo(jid, '%s#%s' % (node, ver)) + + for ext in exts: + qq=q[ext] + if qq.queried==0: + # do query for node+version+ext triple + qq.queried=1 + account.discoverInfo(jid, '%s#%s' % (node, ext)) + +gajim.capscache = CapsCache(gajim.logger) + +class ConnectionCaps(object): + ''' This class highly depends on that it is a part of Connection class. ''' + def _capsPresenceCB(self, con, presence): + ''' Handle incoming presence stanzas... This is a callback + for xmpp registered in connection_handlers.py''' + + # get the caps element + caps=presence.getTag('c') + if not caps: return + + node, ver=caps['node'], caps['ver'] + if node is None or ver is None: + # improper caps in stanza, ignoring + return + + try: + exts=caps['ext'].split(' ') + except AttributeError: + # no exts means no exts, a perfectly valid case + exts=[] + + # we will put these into proper Contact object and ask + # for disco... so that disco will learn how to interpret + # these caps + + jid=str(presence.getFrom()) + + # start disco query... + gajim.capscache.preload(self, jid, node, ver, exts) + + contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) + if contact in [None, []]: + return # TODO: a way to put contact not-in-roster into Contacts + elif isinstance(contact, list): + contact = contact[0] + + # overwriting old data + contact.caps_node=node + contact.caps_ver=ver + contact.caps_exts=exts + + def _capsDiscoCB(self, jid, node, identities, features, data): + contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) + if not contact: return + if not contact.caps_node: return # we didn't asked for that? + if not node.startswith(contact.caps_node+'#'): return + node, ext = node.split('#') + if ext==contact.caps_ver: # this can be also version (like '0.9') + exts=None + else: + exts=(ext,) + + # if we don't have this info already... + caps=gajim.capscache[(node, contact.caps_ver, exts)] + if caps.queried==2: return + + identities=set((i['category'], i['type'], i.get('name')) for i in identities) + caps.update(identities, features) + diff --git a/src/common/check_paths.py b/src/common/check_paths.py index 600855aab..b7f06046d 100644 --- a/src/common/check_paths.py +++ b/src/common/check_paths.py @@ -42,6 +42,7 @@ def create_log_db(): # logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code # jids.jid text column will be JID if TC-related, room_jid if GC-related, # ROOM_JID/nick if pm-related. + # also check optparser.py, which updates databases on gajim updates cur.executescript( ''' CREATE TABLE jids( @@ -74,6 +75,12 @@ def create_log_db(): ); CREATE INDEX idx_logs_jid_id_kind ON logs (jid_id, kind); + + CREATE TABLE caps_cache ( + node TEXT, + ver TEXT, + ext TEXT, + data BLOB); ''' ) diff --git a/src/common/commands.py b/src/common/commands.py index 4fdb6d4d7..802095873 100644 --- a/src/common/commands.py +++ b/src/common/commands.py @@ -236,12 +236,42 @@ class LeaveGroupchatsCommand(AdHocCommand): return False +class ForwardMessagesCommand(AdHocCommand): + # http://www.xmpp.org/extensions/xep-0146.html#forward + commandnode = 'forward-messages' + commandname = _('Forward unread messages') + + @staticmethod + def isVisibleFor(samejid): + ''' Change status is visible only if the entity has the same bare jid. ''' + return samejid + + def execute(self, request): + account = self.connection.name + # Forward messages + events = gajim.events.get_events(account, types=['chat', 'normal']) + j, resource = gajim.get_room_and_nick_from_fjid(self.jid) + for jid in events: + for event in events[jid]: + self.connection.send_message(j, event.parameters[0], '', + type=event.type_, subject=event.parameters[1], + resource=resource, forward_from=jid) + + # Inform other client of completion + response, cmd = self.buildResponse(request, status = 'completed') + cmd.addChild('note', {}, _('All unread messages have been forwarded.')) + + self.connection.connection.send(response) + + return False # finish the session + class ConnectionCommands: ''' This class depends on that it is a part of Connection() class. ''' def __init__(self): # a list of all commands exposed: node -> command class self.__commands = {} - for cmdobj in (ChangeStatusCommand, LeaveGroupchatsCommand): + for cmdobj in (ChangeStatusCommand, ForwardMessagesCommand, + LeaveGroupchatsCommand): self.__commands[cmdobj.commandnode] = cmdobj # a list of sessions; keys are tuples (jid, sessionid, node) diff --git a/src/common/config.py b/src/common/config.py index 028d8f031..f17c51c42 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -7,6 +7,7 @@ ## Copyright (C) 2005 Travis Shirk ## Copyright (C) 2005 Norman Rasmussen ## Copyright (C) 2006 Stefan Bethge +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -81,7 +82,7 @@ class Config: 'markedmsgcolor': [ opt_color, '#ff8080', '', True ], 'urlmsgcolor': [ opt_color, '#0000ff', '', True ], 'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ], - 'roster_theme': [ opt_str, 'gtk+', '', True ], + 'roster_theme': [ opt_str, _('default'), '', True ], 'saveposition': [ opt_bool, True ], 'mergeaccounts': [ opt_bool, False, '', True ], 'sort_by_show': [ opt_bool, True, '', True ], @@ -182,12 +183,12 @@ class Config: 'tooltip_avatar_height': [opt_int, 125], 'vcard_avatar_width': [opt_int, 200], 'vcard_avatar_height': [opt_int, 200], + 'notification_preview_message': [opt_bool, True, _('Preview new messages in notification popup?')], 'notification_position_x': [opt_int, -1], 'notification_position_y': [opt_int, -1], 'notification_avatar_width': [opt_int, 48], 'notification_avatar_height': [opt_int, 48], 'muc_highlight_words': [opt_str, '', _('A semicolon-separated list of words that will be highlighted in group chats.')], - 'minimize_autojoined_rooms': [opt_bool, False, _('If True, autojoined bookmarked rooms will be minimized on startup.')], 'quit_on_roster_x_button': [opt_bool, False, _('If True, quits Gajim when X button of Window Manager is clicked. This setting is taken into account only if trayicon is used.')], 'check_if_gajim_is_default': [opt_bool, True, _('If True, Gajim will check if it\'s the default jabber client on each startup.')], 'show_unread_tab_icon': [opt_bool, False, _('If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated.')], @@ -211,8 +212,7 @@ class Config: _('Controls the window where new messages are placed.\n\'always\' - All messages are sent to a single window.\n\'never\' - All messages get their own window.\n\'peracct\' - Messages for each account are sent to a specific window.\n\'pertype\' - Each message type (e.g., chats vs. groupchats) are sent to a specific window. Note, changing this option requires restarting Gajim before the changes will take effect.')], 'show_avatar_in_chat': [opt_bool, True, _('If False, you will no longer see the avatar in the chat window.')], 'escape_key_closes': [opt_bool, True, _('If True, pressing the escape key closes a tab/window.')], - 'always_hide_groupchat_buttons': [opt_bool, False, _('Hides the buttons in group chat window.')], - 'always_hide_chat_buttons': [opt_bool, False, _('Hides the buttons in two persons chat window.')], + 'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')], 'hide_groupchat_banner': [opt_bool, False, _('Hides the banner in a group chat window')], 'hide_chat_banner': [opt_bool, False, _('Hides the banner in two persons chat window')], 'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the group chat occupants list in group chat window.')], @@ -228,6 +228,7 @@ class Config: 'scroll_roster_to_last_message': [opt_bool, True, _('If True, Gajim will scroll and select the contact who sent you the last message, if chat window is not already opened.')], 'use_latex': [opt_bool, False, _('If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window.')], 'change_status_window_timeout': [opt_int, 15, _('Time of inactivity needed before the change status window closes down.')], + 'max_conversation_lines': [opt_int, 500, _('Maximum number of lines that are printed in conversations. Oldest lines are cleared.')], } __options_per_key = { @@ -260,6 +261,7 @@ class Config: 'gpgpassword': [ opt_str, '' ], 'sync_with_global_status': [ opt_bool, False, ], 'no_log_for': [ opt_str, '' ], + 'minimized_gc': [ opt_str, '' ], 'attached_gpg_keys': [ opt_str, '' ], 'keep_alives_enabled': [ opt_bool, True], # send keepalive every N seconds of inactivity @@ -387,8 +389,8 @@ class Config: themes_default = { # sorted alphanum - 'gtk+': [ '', '', '', 'B', '', '','', 'I', '', '', '', '', '','', '', - 'B' ], + _('default'): [ '', '', '', 'B', '', '','', 'I', '', '', '', '', '','', + '', 'B' ], _('green'): [ '', '#94aa8c', '', 'B', '#0000ff', '#eff3e7', '', 'I', '#000000', '', '', '', '', diff --git a/src/common/configpaths.py b/src/common/configpaths.py index f7e09a686..da41fb8d7 100644 --- a/src/common/configpaths.py +++ b/src/common/configpaths.py @@ -25,6 +25,11 @@ def fse(s): '''Convert from filesystem encoding if not already Unicode''' return unicode(s, sys.getfilesystemencoding()) +def windowsify(s): + if os.name == 'nt': + return s.capitalize() + return s + class ConfigPaths: def __init__(self, root=None): self.root = root @@ -68,51 +73,47 @@ class ConfigPaths: for key in self.paths.iterkeys(): yield (key, self[key]) -def windowsify(s): - if os.name == 'nt': - return s.capitalize() - return s + def init(self, root = None): + if root is not None: + self.root = root -def init(): - paths = ConfigPaths() + # LOG is deprecated + k = ( 'LOG', 'LOG_DB', 'VCARD', 'AVATAR', 'MY_EMOTS', + 'MY_ICONSETS' ) + v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons', + u'iconsets') - # LOG is deprecated - k = ( 'LOG', 'LOG_DB', 'VCARD', 'AVATAR', 'MY_EMOTS' ) - v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons') + if os.name == 'nt': + v = map(lambda x: x.capitalize(), v) - if os.name == 'nt': - v = map(lambda x: x.capitalize(), v) + for n, p in zip(k, v): + self.add_from_root(n, p) - for n, p in zip(k, v): - paths.add_from_root(n, p) + self.add('DATA', os.path.join(u'..', windowsify(u'data'))) + self.add('HOME', fse(os.path.expanduser('~'))) + self.add('TMP', fse(tempfile.gettempdir())) - paths.add('DATA', os.path.join(u'..', windowsify(u'data'))) - paths.add('HOME', fse(os.path.expanduser('~'))) - paths.add('TMP', fse(tempfile.gettempdir())) + try: + import svn_config + svn_config.configure(self) + except (ImportError, AttributeError): + pass - try: - import svn_config - svn_config.configure(paths) - except (ImportError, AttributeError): - pass + # for k, v in paths.iteritems(): + # print "%s: %s" % (repr(k), repr(v)) - # for k, v in paths.iteritems(): - # print "%s: %s" % (repr(k), repr(v)) + def init_profile(self, profile): + conffile = windowsify(u'config') + pidfile = windowsify(u'gajim') - return paths + if len(profile) > 0: + conffile += u'.' + profile + pidfile += u'.' + profile + pidfile += u'.pid' + self.add_from_root('CONFIG_FILE', conffile) + self.add_from_root('PID_FILE', pidfile) -gajimpaths = init() + # for k, v in paths.iteritems(): + # print "%s: %s" % (repr(k), repr(v)) -def init_profile(profile, paths=gajimpaths): - conffile = windowsify(u'config') - pidfile = windowsify(u'gajim') - - if len(profile) > 0: - conffile += u'.' + profile - pidfile += u'.' + profile - pidfile += u'.pid' - paths.add_from_root('CONFIG_FILE', conffile) - paths.add_from_root('PID_FILE', pidfile) - - # for k, v in paths.iteritems(): - # print "%s: %s" % (repr(k), repr(v)) +gajimpaths = ConfigPaths() diff --git a/src/common/connection.py b/src/common/connection.py index 00a17a54e..0284e1c39 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -6,6 +6,7 @@ ## Copyright (C) 2005-2006 Nikos Kouremenos ## Copyright (C) 2005-2006 Dimitur Kirov ## Copyright (C) 2005-2006 Travis Shirk +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -36,6 +37,7 @@ from common import helpers from common import gajim from common import GnuPG from common import passwords +from common import exceptions from connection_handlers import * USE_GPG = GnuPG.USE_GPG @@ -48,12 +50,50 @@ log = logging.getLogger('gajim.c.connection') import gtkgui_helpers +ssl_error = { +2: "Unable to get issuer certificate", +3: "Unable to get certificate CRL", +4: "Unable to decrypt certificate's signature", +5: "Unable to decrypt CRL's signature", +6: "Unable to decode issuer public key", +7: "Certificate signature failure", +8: "CRL signature failure", +9: "Certificate is not yet valid", +10: "Certificate has expired", +11: "CRL is not yet valid", +12: "CRL has expired", +13: "Format error in certificate's notBefore field", +14: "Format error in certificate's notAfter field", +15: "Format error in CRL's lastUpdate field", +16: "Format error in CRL's nextUpdate field", +17: "Out of memory", +18: "Self signed certificate in certificate chain", +19: "Unable to get local issuer certificate", +20: "Unable to verify the first certificate", +21: "Unable to verify the first certificate", +22: "Certificate chain too long", +23: "Certificate revoked", +24: "Invalid CA certificate", +25: "Path length constraint exceeded", +26: "Unsupported certificate purpose", +27: "Certificate not trusted", +28: "Certificate rejected", +29: "Subject issuer mismatch", +30: "Authority and subject key identifier mismatch", +31: "Authority and issuer serial number mismatch", +32: "Key usage does not include certificate signing", +50: "Application verification failure" +} class Connection(ConnectionHandlers): '''Connection class''' def __init__(self, name): ConnectionHandlers.__init__(self) self.name = name - self.connected = 0 # offline + # self.connected: + # 0=>offline, + # 1=>connection in progress, + # 2=>authorised + self.connected = 0 self.connection = None # xmpppy ClientCommon instance # this property is used to prevent double connections self.last_connection = None # last ClientCommon instance @@ -93,6 +133,10 @@ class Connection(ConnectionHandlers): self.pep_supported = False # Do we continue connection when we get roster (send presence,get vcard...) self.continue_connect_info = None + # To know the groupchat jid associated with a sranza ID. Useful to + # request vcard or os info... to a real JID but act as if it comes from + # the fake jid + self.groupchat_jids = {} # {ID : groupchat_jid} if USE_GPG: self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) gajim.config.set('usegpg', True) @@ -210,10 +254,7 @@ class Connection(ConnectionHandlers): % (data[0], data[3]))) return is_form = data[2] - if is_form: - conf = data[1] - else: - conf = data[1].asDict() + conf = data[1] self.dispatch('NEW_ACC_CONNECTED', (conf, is_form)) return if not data[1]: # wrong answer @@ -222,10 +263,7 @@ class Connection(ConnectionHandlers): (data[0], data[3]))) return is_form = data[2] - if is_form: - conf = data[1] - else: - conf = data[1].asDict() + conf = data[1] self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form)) elif realm == common.xmpp.NS_PRIVACY: if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED: @@ -262,6 +300,8 @@ class Connection(ConnectionHandlers): self.dispatch('STANZA_SENT', unicode(data)) def select_next_host(self, hosts): + '''Chooses best 'real' host basing on the SRV priority and weight data; + more info in RFC2782''' hosts_best_prio = [] best_prio = 65535 sum_weight = 0 @@ -440,72 +480,20 @@ class Connection(ConnectionHandlers): name = gajim.config.get_per('accounts', self.name, 'name') hostname = gajim.config.get_per('accounts', self.name, 'hostname') self.connection = con - - fpr_good = self._check_fingerprint(con, con_type) - if fpr_good == False: - self.disconnect(on_purpose = True) - self.dispatch('STATUS', 'offline') - self.dispatch('CONNECTION_LOST', - (_('Security error connecting to "%s"') % self._hostname, - _("The server's key has changed, or someone is trying to hack your connection."))) - if self.on_connect_auth: - self.on_connect_auth(None) - self.on_connect_auth = None - return - - if fpr_good == None: - log.warning(_("Unable to check fingerprint for %s. Connection could be insecure."), hostname) - - if fpr_good == True: - log.info("Fingerprint found and matched for %s.", hostname) - + try: + errnum = con.Connection.ssl_errnum + except AttributeError: + errnum = -1 # we don't have an errnum + if errnum > 0: + # FIXME: tell the user that the certificat is untrusted, and ask him what to do + try: + log.warning("The authenticity of the "+hostname+" certificate could be unvalid.\nSSL Error: "+ssl_error[errnum]) + except KeyError: + log.warning("Unknown SSL error: %d" % errnum) con.auth(name, self.password, self.server_resource, 1, self.__on_auth) return True - def _check_fingerprint(self, con, con_type): - fpr_good = None # None: Unable to check fpr, False: mismatch, True: match - - # FIXME: not tidy - if not common.xmpp.transports_nb.USE_PYOPENSSL: return None - - # FIXME: find a more permanent place for loading servers.xml - servers_xml = os.path.join(gajim.DATA_DIR, 'other', 'servers.xml') - servers = gtkgui_helpers.parse_server_xml(servers_xml) - servers = dict(map(lambda e: (e[0], e), servers)) - - hostname = gajim.config.get_per('accounts', self.name, 'hostname') - - try: - log.debug("con: %s", con) - log.debug("con.Connection: %s", con.Connection) - log.debug("con.Connection.serverDigestSHA1: %s", con.Connection.serverDigestSHA1) - log.debug("con.Connection.serverDigestMD5: %s", con.Connection.serverDigestMD5) - sha1 = gtkgui_helpers.HashDigest('sha1', con.Connection.serverDigestSHA1) - md5 = gtkgui_helpers.HashDigest('md5', con.Connection.serverDigestMD5) - log.debug("sha1: %s", repr(sha1)) - log.debug("md5: %s", repr(md5)) - - sv = servers.get(hostname) - if sv: - for got in (sha1, md5): - expected = sv[2]['digest'].get(got.algo) - if expected: - fpr_good = (got == expected) - break - - except AttributeError: - if con_type in ('ssl', 'tls'): - log.error(_("Missing fingerprint in SSL connection to %s") + ':', hostname, exc_info=True) - # fpr_good = False # FIXME: enable this when sequence is sorted - else: - log.debug("Connection to %s doesn't seem to have a fingerprint:", hostname, exc_info=True) - - if fpr_good == False: - log.error(_("Fingerprint mismatch for %s: Got %s, expected %s"), hostname, got, expected) - - return fpr_good - def _register_handlers(self, con, con_type): self.peerhost = con.get_peerhost() # notify the gui about con_type @@ -566,14 +554,14 @@ class Connection(ConnectionHandlers): iq = common.xmpp.Iq('get', to = pingTo.get_full_jid()) iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING) def _on_response(resp): - timePong = time.time() + timePong = time_time() if not common.xmpp.isResultNode(resp): self.dispatch('PING_ERROR', (pingTo)) return timeDiff = round(timePong - timePing,2) self.dispatch('PING_REPLY', (pingTo, timeDiff)) self.dispatch('PING_SENT', (pingTo)) - timePing = time.time() + timePing = time_time() self.connection.SendAndCallForResponse(iq, _on_response) def get_active_default_lists(self): @@ -765,6 +753,12 @@ class Connection(ConnectionHandlers): self.on_purpose = False self.server_resource = gajim.config.get_per('accounts', self.name, 'resource') + # All valid resource substitution strings should be added to this hash. + if self.server_resource: + self.server_resource = Template(self.server_resource).\ + safe_substitute({ + 'hostname': socket.gethostname() + }) self.connect_and_init(show, msg, signed) elif show == 'offline': @@ -825,8 +819,8 @@ class Connection(ConnectionHandlers): self.connection.send(msg_iq) def send_message(self, jid, msg, keyID, type = 'chat', subject='', - chatstate = None, msg_id = None, composing_jep = None, resource = None, - user_nick = None, xhtml = None): + chatstate = None, msg_id = None, composing_xep = None, resource = None, + user_nick = None, xhtml = None, forward_from = None): if not self.connection: return 1 if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'): @@ -850,7 +844,7 @@ class Connection(ConnectionHandlers): ' ([This message is *encrypted* (See :JEP:`27`])' else: # Encryption failed, do not send message - tim = time.localtime() + tim = localtime() self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim)) return 3 if msgtxt and not xhtml and gajim.config.get( @@ -879,12 +873,12 @@ class Connection(ConnectionHandlers): # please note that the only valid tag inside a message containing a # tag is the active event if chatstate is not None: - if (composing_jep == 'JEP-0085' or not composing_jep) and \ - composing_jep != 'asked_once': - # JEP-0085 + if (composing_xep == 'XEP-0085' or not composing_xep) and \ + composing_xep != 'asked_once': + # XEP-0085 msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES) - if composing_jep in ('JEP-0022', 'asked_once') or not composing_jep: - # JEP-0022 + if composing_xep in ('XEP-0022', 'asked_once') or not composing_xep: + # XEP-0022 chatstate_node = msg_iq.setTag('x', namespace = common.xmpp.NS_EVENT) if not msgtxt: # when no , add @@ -895,21 +889,30 @@ class Connection(ConnectionHandlers): if chatstate is 'composing' or msgtxt: chatstate_node.addChild(name = 'composing') + if forward_from: + addresses = msg_iq.addChild('addresses', + namespace=common.xmpp.NS_ADDRESS) + addresses.addChild('address', attrs = {'type': 'ofrom', + 'jid': forward_from}) self.connection.send(msg_iq) - no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\ - .split() - ji = gajim.get_jid_without_resource(jid) - if self.name not in no_log_for and ji not in no_log_for: - log_msg = msg - if subject: - log_msg = _('Subject: %s\n%s') % (subject, msg) - if log_msg: - if type == 'chat': - kind = 'chat_msg_sent' - else: - kind = 'single_msg_sent' - gajim.logger.write(kind, jid, log_msg) - self.dispatch('MSGSENT', (jid, msg, keyID)) + if not forward_from: + no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\ + .split() + ji = gajim.get_jid_without_resource(jid) + if self.name not in no_log_for and ji not in no_log_for: + log_msg = msg + if subject: + log_msg = _('Subject: %s\n%s') % (subject, msg) + if log_msg: + if type == 'chat': + kind = 'chat_msg_sent' + else: + kind = 'single_msg_sent' + try: + gajim.logger.write(kind, jid, log_msg) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) + self.dispatch('MSGSENT', (jid, msg, keyID)) def send_stanza(self, stanza): ''' send a stanza untouched ''' @@ -1059,7 +1062,9 @@ class Connection(ConnectionHandlers): def account_changed(self, new_name): self.name = new_name - def request_last_status_time(self, jid, resource): + def request_last_status_time(self, jid, resource, groupchat_jid=None): + '''groupchat_jid is used when we want to send a request to a real jid + and act as if the answer comes from the groupchat_jid''' if not self.connection: return to_whom_jid = jid @@ -1067,9 +1072,15 @@ class Connection(ConnectionHandlers): to_whom_jid += '/' + resource iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\ common.xmpp.NS_LAST) + id = self.connection.getAnID() + iq.setID(id) + if groupchat_jid: + self.groupchat_jids[id] = groupchat_jid self.connection.send(iq) - def request_os_info(self, jid, resource): + def request_os_info(self, jid, resource, groupchat_jid=None): + '''groupchat_jid is used when we want to send a request to a real jid + and act as if the answer comes from the groupchat_jid''' if not self.connection: return # If we are invisible, do not request @@ -1081,6 +1092,10 @@ class Connection(ConnectionHandlers): to_whom_jid += '/' + resource iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\ common.xmpp.NS_VERSION) + id = self.connection.getAnID() + iq.setID(id) + if groupchat_jid: + self.groupchat_jids[id] = groupchat_jid self.connection.send(iq) def get_settings(self): @@ -1088,7 +1103,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='get') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq3 = iq2.addChild(name='gajim', namespace='gajim:prefs') self.connection.send(iq) @@ -1098,7 +1113,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='get') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq2.addChild(name='storage', namespace='storage:bookmarks') self.connection.send(iq) @@ -1107,12 +1122,13 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='set') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq3 = iq2.addChild(name='storage', namespace='storage:bookmarks') for bm in self.bookmarks: iq4 = iq3.addChild(name = "conference") iq4.setAttr('jid', bm['jid']) iq4.setAttr('autojoin', bm['autojoin']) + iq4.setAttr('minimize', bm['minimize']) iq4.setAttr('name', bm['name']) # Only add optional elements if not empty # Note: need to handle both None and '' as empty @@ -1131,7 +1147,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='get') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq2.addChild(name='storage', namespace='storage:rosternotes') self.connection.send(iq) @@ -1140,7 +1156,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='set') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq3 = iq2.addChild(name='storage', namespace='storage:rosternotes') for jid in self.annotations.keys(): if self.annotations[jid]: @@ -1155,7 +1171,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='get') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq2.addChild(name='storage', namespace='storage:metacontacts') id = self.connection.getAnID() iq.setID(id) @@ -1167,7 +1183,7 @@ class Connection(ConnectionHandlers): if not self.connection: return iq = common.xmpp.Iq(typ='set') - iq2 = iq.addChild(name='query', namespace='jabber:iq:private') + iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE) iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts') for tag in tags_list: for data in tags_list[tag]: @@ -1264,7 +1280,7 @@ class Connection(ConnectionHandlers): self.connection.send(p) # Save the time we quit to avoid duplicate logs AND be faster than # get that date from DB - self.last_history_line[jid] = time.time() + self.last_history_line[jid] = time_time() def gc_set_role(self, room_jid, nick, role, reason = ''): '''role is for all the life of the room so it's based on nick''' @@ -1419,7 +1435,8 @@ class Connection(ConnectionHandlers): return df = [] for item in tag.getTags('item'): - f = {} + # We also show attributes. jid is there + f = item.attrs for i in item.getPayload(): f[i.getName()] = i.getData() df.append(f) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index d1de9bdbc..5d95db71e 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -18,13 +18,13 @@ ## import os -import time import base64 import sha import socket import sys -from time import localtime, strftime, gmtime +from time import (altzone, daylight, gmtime, localtime, mktime, strftime, + time as time_time, timezone, tzname) from calendar import timegm import socks5 @@ -35,8 +35,10 @@ from common import helpers from common import gajim from common import atom from common import pep +from common import exceptions from common.commands import ConnectionCommands from common.pubsub import ConnectionPubSub +from common.caps import ConnectionCaps STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', 'invisible', 'error'] @@ -414,7 +416,7 @@ class ConnectionBytestream: def _ResultCB(self, con, iq_obj): gajim.log.debug('_ResultCB') - # if we want to respect jep-0065 we have to check for proxy + # if we want to respect xep-0065 we have to check for proxy # activation result in any result iq real_id = unicode(iq_obj.getAttr('id')) if real_id[:3] != 'au_': @@ -562,6 +564,7 @@ class ConnectionBytestream: file_props['sender'] = helpers.get_full_jid_from_iq(iq_obj) file_props['request-id'] = unicode(iq_obj.getAttr('id')) file_props['sid'] = unicode(si.getAttr('id')) + file_props['transfered_size'] = [] gajim.socks5queue.add_file_props(self.name, file_props) self.dispatch('FILE_REQUEST', (jid, file_props)) raise common.xmpp.NodeProcessed @@ -588,12 +591,12 @@ class ConnectionBytestream: class ConnectionDisco: ''' hold xmpppy handlers and public methods for discover services''' def discoverItems(self, jid, node = None, id_prefix = None): - '''According to JEP-0030: jid is mandatory, + '''According to XEP-0030: jid is mandatory, name, node, action is optional.''' self._discover(common.xmpp.NS_DISCO_ITEMS, jid, node, id_prefix) def discoverInfo(self, jid, node = None, id_prefix = None): - '''According to JEP-0030: + '''According to XEP-0030: For identity: category, type is mandatory, name is optional. For feature: var is mandatory''' self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix) @@ -702,6 +705,10 @@ class ConnectionDisco: def _DiscoverItemsGetCB(self, con, iq_obj): gajim.log.debug('DiscoverItemsGetCB') node = iq_obj.getTagAttr('query', 'node') + if node is None: + result = iq_obj.buildReply('result') + self.connection.send(result) + raise common.xmpp.NodeProcessed if node==common.xmpp.NS_COMMANDS: self.commandListQuery(con, iq_obj) raise common.xmpp.NodeProcessed @@ -746,6 +753,10 @@ class ConnectionDisco: if (node is None or extension == 'xhtml') and not gajim.config.get('ignore_incoming_xhtml'): q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM}) + if node is None: + q.addChild('feature', attrs = {'var': common.xmpp.NS_PING}) + q.addChild('feature', attrs = {'var': common.xmpp.NS_TIME_REVISED}) + if q.getChildren(): self.connection.send(iq) raise common.xmpp.NodeProcessed @@ -757,7 +768,7 @@ class ConnectionDisco: def _DiscoverInfoCB(self, con, iq_obj): gajim.log.debug('DiscoverInfoCB') - # According to JEP-0030: + # According to XEP-0030: # For identity: category, type is mandatory, name is optional. # For feature: var is mandatory identities, features, data = [], [], [] @@ -775,11 +786,13 @@ class ConnectionDisco: attr = {} for key in i.getAttrs().keys(): attr[key] = i.getAttr(key) - if attr.has_key('category') and attr['category'] in ('gateway', 'headline')\ - and attr.has_key('type'): + if attr.has_key('category') and \ + attr['category'] in ('gateway', 'headline') and \ + attr.has_key('type'): transport_type = attr['type'] - if attr.has_key('category') and attr['category'] == 'conference' \ - and attr.has_key('type') and attr['type'] == 'text': + if attr.has_key('category') and \ + attr['category'] == 'conference' and \ + attr.has_key('type') and attr['type'] == 'text': is_muc = True identities.append(attr) elif i.getName() == 'feature': @@ -796,6 +809,9 @@ class ConnectionDisco: identities = [{'category': 'server', 'type': 'im', 'name': node}] if id[0] == 'p': if jid == gajim.config.get_per('accounts', self.name, 'hostname'): + if features.__contains__(common.xmpp.NS_GMAILNOTIFY): + gajim.gmail_domains.append(jid) + self.request_gmail_notifications() for identity in identities: if identity['category'] == 'pubsub' and identity['type'] == \ 'pep': @@ -811,16 +827,17 @@ class ConnectionDisco: self.available_transports[transport_type].append(jid) else: self.available_transports[transport_type] = [jid] + self.dispatch('AGENT_INFO_INFO', (jid, node, identities, features, data)) + self._capsDiscoCB(jid, node, identities, features, data) class ConnectionVcard: def __init__(self): self.vcard_sha = None self.vcard_shas = {} # sha of contacts self.room_jids = [] # list of gc jids so that vcard are saved in a folder - self.groupchat_jids = {} # {ID : groupchat_jid} - + def add_sha(self, p, send_caps = True): c = p.setTag('x', namespace = common.xmpp.NS_VCARD_UPDATE) if self.vcard_sha is not None: @@ -830,7 +847,7 @@ class ConnectionVcard: return p def add_caps(self, p): - ''' advertise our capabilities in presence stanza (jep-0115)''' + ''' advertise our capabilities in presence stanza (xep-0115)''' c = p.setTag('c', namespace = common.xmpp.NS_CAPS) c.setAttr('node', 'http://gajim.org/caps') ext = [] @@ -880,9 +897,12 @@ class ConnectionVcard: path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick) else: path_to_file = path - fil = open(path_to_file, 'w') - fil.write(str(card)) - fil.close() + try: + fil = open(path_to_file, 'w') + fil.write(str(card)) + fil.close() + except IOError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) def get_cached_vcard(self, fjid, is_fake_jid = False): '''return the vcard as a dict @@ -1042,7 +1062,7 @@ class ConnectionVcard: elif self.awaiting_answers[id][0] == METACONTACTS_ARRIVED: if iq_obj.getType() == 'result': # Metacontact tags - # http://www.jabber.org/jeps/jep-XXXX.html + # http://www.xmpp.org/extensions/xep-0209.html meta_list = {} query = iq_obj.getTag('query') storage = query.getTag('storage') @@ -1171,12 +1191,13 @@ class ConnectionVcard: #('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...}) self.dispatch('VCARD', vcard) -class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub): +class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps): def __init__(self): ConnectionVcard.__init__(self) ConnectionBytestream.__init__(self) ConnectionCommands.__init__(self) ConnectionPubSub.__init__(self) + self.gmail_url=None # List of IDs we are waiting answers for {id: (type_of_request, data), } self.awaiting_answers = {} # List of IDs that will produce a timeout is answer doesn't arrive @@ -1229,7 +1250,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, def _PrivateCB(self, con, iq_obj): ''' - Private Data (JEP 048 and 049) + Private Data (XEP 048 and 049) ''' gajim.log.debug('PrivateCB') query = iq_obj.getTag('query') @@ -1238,18 +1259,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ns = storage.getNamespace() if ns == 'storage:bookmarks': # Bookmarked URLs and Conferences - # http://www.jabber.org/jeps/jep-0048.html + # http://www.xmpp.org/extensions/xep-0048.html confs = storage.getTags('conference') for conf in confs: autojoin_val = conf.getAttr('autojoin') if autojoin_val is None: # not there (it's optional) autojoin_val = False + minimize_val = conf.getAttr('minimize') + if minimize_val is None: # not there (it's optional) + minimize_val = False print_status = conf.getTagData('print_status') if not print_status: print_status = conf.getTagData('show_status') bm = {'name': conf.getAttr('name'), 'jid': conf.getAttr('jid'), 'autojoin': autojoin_val, + 'minimize': minimize_val, 'password': conf.getTagData('password'), 'nick': conf.getTagData('nick'), 'print_status': print_status} @@ -1259,7 +1284,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, elif ns == 'gajim:prefs': # Preferences data - # http://www.jabber.org/jeps/jep-0049.html + # http://www.xmpp.org/extensions/xep-0049.html #TODO: implement this pass elif ns == 'storage:rosternotes': @@ -1279,7 +1304,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ns = storage_tag.getNamespace() if ns == 'storage:metacontacts': self.metacontacts_supported = False - # Private XML Storage (JEP49) is not supported by server + # Private XML Storage (XEP49) is not supported by server # Continue connecting self.connection.initRoster() @@ -1329,7 +1354,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, seconds = int(seconds) except: return - who = helpers.get_full_jid_from_iq(iq_obj) + id = iq_obj.getID() + if id in self.groupchat_jids: + who = self.groupchat_jids[id] + del self.groupchat_jids[id] + else: + who = helpers.get_full_jid_from_iq(iq_obj) jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who) self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, seconds, status)) @@ -1344,7 +1374,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, client_info += ' ' + qp.getTag('version').getData() if qp.getTag('os'): os_info += qp.getTag('os').getData() - who = helpers.get_full_jid_from_iq(iq_obj) + id = iq_obj.getID() + if id in self.groupchat_jids: + who = self.groupchat_jids[id] + del self.groupchat_jids[id] + else: + who = helpers.get_full_jid_from_iq(iq_obj) jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who) self.dispatch('OS_INFO', (jid_stripped, resource, client_info, os_info)) @@ -1352,18 +1387,21 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, gajim.log.debug('TimeCB') iq_obj = iq_obj.buildReply('result') qp = iq_obj.getTag('query') - qp.setTagData('utc', strftime("%Y%m%dT%T", gmtime())) - qp.setTagData('tz', strftime("%Z", gmtime())) - qp.setTagData('display', strftime("%c", localtime())) + qp.setTagData('utc', strftime('%Y%m%dT%T', gmtime())) + qp.setTagData('tz', tzname[daylight]) + qp.setTagData('display', strftime('%c', localtime())) self.connection.send(iq_obj) raise common.xmpp.NodeProcessed def _TimeRevisedCB(self, con, iq_obj): gajim.log.debug('TimeRevisedCB') iq_obj = iq_obj.buildReply('result') - qp = iq_obj.setTag('time') - qp.setTagData('utc', strftime("%Y-%m-%dT%TZ", gmtime())) - qp.setTagData('tzo', "%+03d:00"% (-time.timezone/(60*60))) + qp = iq_obj.setTag('time', + namespace=common.xmpp.NS_TIME_REVISED) + qp.setTagData('utc', strftime('%Y-%m-%dT%TZ', gmtime())) + zone = -(timezone, altzone)[daylight] / 60 + tzo = (zone / 60, abs(zone % 60)) + qp.setTagData('tzo', '%+03d:%02d' % (tzo)) self.connection.send(iq_obj) raise common.xmpp.NodeProcessed @@ -1386,6 +1424,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, '''Called when we receive results from Querying the server for mail messages in gmail account''' if not gm.getTag('mailbox'): return + self.gmail_url = gm.getTag('mailbox').getAttr('url') if gm.getTag('mailbox').getNamespace() == common.xmpp.NS_GMAILNOTIFY: newmsgs = gm.getTag('mailbox').getAttr('total-matched') if newmsgs != '0': @@ -1425,10 +1464,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, mtype = msg.getType() subject = msg.getSubject() # if not there, it's None tim = msg.getTimestamp() - tim = time.strptime(tim, '%Y%m%dT%H:%M:%S') - tim = time.localtime(timegm(tim)) + tim = helpers.datetime_tuple(tim) + tim = localtime(timegm(tim)) frm = helpers.get_full_jid_from_iq(msg) jid = helpers.get_jid_from_iq(msg) + addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS) + # Be sure it comes from one of our resource, else ignore address element + if addressTag and jid == gajim.get_jid_from_account(self.name): + address = addressTag.getTag('address', attrs={'type': 'ofrom'}) + if address: + frm = address.getAttr('jid') + jid = gajim.get_jid_without_resource(frm) no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for') if not no_log_for: @@ -1446,10 +1492,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, invite = None delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None msg_id = None - composing_jep = None + composing_xep = None # FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED # invitation - # stanza (MUC JEP) remove in 2007, as we do not do NOT RECOMMENDED + # stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED xtags = msg.getTags('x') for xtag in xtags: if xtag.getNamespace() == common.xmpp.NS_CONFERENCE and not invite: @@ -1458,22 +1504,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, return # chatstates - look for chatstate tags in a message if not delayed if not delayed: - composing_jep = False + composing_xep = False children = msg.getChildren() for child in children: if child.getNamespace() == 'http://jabber.org/protocol/chatstates': chatstate = child.getName() - composing_jep = 'JEP-0085' + composing_xep = 'XEP-0085' break - # No JEP-0085 support, fallback to JEP-0022 + # No XEP-0085 support, fallback to XEP-0022 if not chatstate: chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT) if chatstate_child: chatstate = 'active' - composing_jep = 'JEP-0022' + composing_xep = 'XEP-0022' if not msgtxt and chatstate_child.getTag('composing'): chatstate = 'composing' - # JEP-0172 User Nickname + # XEP-0172 User Nickname user_nick = msg.getTagData('nick') if not user_nick: user_nick = '' @@ -1494,8 +1540,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, error_msg = msgtxt msgtxt = None if self.name not in no_log_for: - gajim.logger.write('error', frm, error_msg, tim = tim, - subject = subject) + try: + gajim.logger.write('error', frm, error_msg, tim = tim, + subject = subject) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt, tim)) return @@ -1507,22 +1556,34 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp)) else: if not msg.getTag('body'): #no + # It could be a config change. See + # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify + if msg.getTag('x'): + statusCode = msg.getStatusCode() + if statusCode != []: + self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode)) return # Ignore message from room in which we are not if not self.last_history_line.has_key(jid): return self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml)) - if self.name not in no_log_for and not int(float(time.mktime(tim)))\ + if self.name not in no_log_for and not int(float(mktime(tim)))\ <= self.last_history_line[jid] and msgtxt: - gajim.logger.write('gc_msg', frm, msgtxt, tim = tim) + try: + gajim.logger.write('gc_msg', frm, msgtxt, tim = tim) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) return elif mtype == 'chat': # it's type 'chat' if not msg.getTag('body') and chatstate is None: #no return if msg.getTag('body') and self.name not in no_log_for and jid not in\ no_log_for and msgtxt: - msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim, - subject = subject) + try: + msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, + tim = tim, subject = subject) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) else: # it's single message if invite is not None: item = invite.getTag('invite') @@ -1533,14 +1594,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, self.dispatch('GC_INVITATION',(frm, jid_from, reason, password)) return if self.name not in no_log_for and jid not in no_log_for and msgtxt: - gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim, - subject = subject) + try: + gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim, + subject = subject) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) mtype = 'normal' treat_as = gajim.config.get('treat_incoming_messages') if treat_as: mtype = treat_as self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, - subject, chatstate, msg_id, composing_jep, user_nick, msghtml)) + subject, chatstate, msg_id, composing_xep, user_nick, msghtml)) # END messageCB def _pubsubEventCB(self, con, msg): @@ -1567,20 +1631,13 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, if items is None: return for item in items.getTags('item'): - # check for event type (for now only one type supported: pubsub.com events) - child = item.getTag('pubsub-message') - if child is not None: - # we have pubsub.com notification - child = child.getTag('feed') - if child is None: continue - - for entry in child.getTags('entry'): - # for each entry in feed (there shouldn't be more than one, - # but to be sure... - self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),)) + entry = item.getTag('entry') + if entry is not None: + # for each entry in feed (there shouldn't be more than one, + # but to be sure... + self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),)) continue # unknown type... probably user has another client who understands that event - raise common.xmpp.NodeProcessed def _presenceCB(self, con, prs): @@ -1609,7 +1666,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, sigTag = None ns_muc_user_x = None avatar_sha = None - # JEP-0172 User Nickname + # XEP-0172 User Nickname user_nick = prs.getTagData('nick') if not user_nick: user_nick = '' @@ -1628,10 +1685,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, avatar_sha = x.getTagData('photo') contact_nickname = x.getTagData('nickname') elif namespace == common.xmpp.NS_DELAY: - # JEP-0091 + # XEP-0091 tim = prs.getTimestamp() - tim = time.strptime(tim, '%Y%m%dT%H:%M:%S') - timestamp = time.localtime(timegm(tim)) + tim = helpers.datetime_tuple(tim) + timestamp = localtime(timegm(tim)) elif namespace == 'http://delx.cjb.net/protocol/roster-subsync': # see http://trac.gajim.org/ticket/326 agent = gajim.get_server_from_jid(jid_stripped) @@ -1664,27 +1721,29 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, if ptype == 'error': errmsg = prs.getError() errcode = prs.getErrorCode() + room_jid, nick = gajim.get_room_and_nick_from_fjid(who) if errcode == '502': # Internal Timeout: self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource, prio, keyID, timestamp, None)) elif errcode == '401': # password required to join - self.dispatch('ERROR', (_('Unable to join group chat'), - _('A password is required to join this group chat.'))) + self.dispatch('GC_PASSWORD_REQUIRED', (room_jid, nick)) elif errcode == '403': # we are banned self.dispatch('ERROR', (_('Unable to join group chat'), - _('You are banned from this group chat.'))) + _('You are banned from group chat %s.') % room_jid)) elif errcode == '404': # group chat does not exist self.dispatch('ERROR', (_('Unable to join group chat'), - _('Such group chat does not exist.'))) + _('Group chat %s does not exist.') % room_jid)) elif errcode == '405': self.dispatch('ERROR', (_('Unable to join group chat'), _('Group chat creation is restricted.'))) elif errcode == '406': self.dispatch('ERROR', (_('Unable to join group chat'), - _('Your registered nickname must be used.'))) + _('Your registered nickname must be used in group chat %s.') \ + % room_jid)) elif errcode == '407': self.dispatch('ERROR', (_('Unable to join group chat'), - _('You are not in the members list.'))) + _('You are not in the members list in groupchat %s.') % \ + room_jid)) elif errcode == '409': # nick conflict # the jid_from in this case is FAKE JID: room_jid/nick # resource holds the bad nick so propose a new one @@ -1692,7 +1751,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, gajim.config.get('gc_proposed_nick_char') room_jid = gajim.get_room_from_fjid(who) self.dispatch('ASK_NEW_NICK', (room_jid, _('Unable to join group chat'), - _('Your desired nickname is in use or registered by another occupant.\nPlease specify another nickname below:'), proposed_nickname)) + _('Your desired nickname in group chat %s is in use or registered by another occupant.\nPlease specify another nickname below:') % room_jid, proposed_nickname)) else: # print in the window the error self.dispatch('ERROR_ANSWER', ('', jid_stripped, errmsg, errcode)) @@ -1709,7 +1768,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, if jid: # we know real jid, save it in db st += ' (%s)' % jid - gajim.logger.write('gcstatus', who, st, show) + try: + gajim.logger.write('gcstatus', who, st, show) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) if avatar_sha or avatar_sha == '': if avatar_sha == '': # contact has no avatar @@ -1728,7 +1790,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, jid = destroy.getAttr('jid') if jid: reason += '\n' + _('You can join this room instead: %s') % jid - statusCode = 'destroyed' + statusCode = ['destroyed'] else: reason = prs.getReason() statusCode = prs.getStatusCode() @@ -1763,10 +1825,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # detect a subscription loop if not self.subscribed_events.has_key(jid_stripped): self.subscribed_events[jid_stripped] = [] - self.subscribed_events[jid_stripped].append(time.time()) + self.subscribed_events[jid_stripped].append(time_time()) block = False if len(self.subscribed_events[jid_stripped]) > 5: - if time.time() - self.subscribed_events[jid_stripped][0] < 5: + if time_time() - self.subscribed_events[jid_stripped][0] < 5: block = True self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:] if block: @@ -1783,10 +1845,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # detect a unsubscription loop if not self.subscribed_events.has_key(jid_stripped): self.subscribed_events[jid_stripped] = [] - self.subscribed_events[jid_stripped].append(time.time()) + self.subscribed_events[jid_stripped].append(time_time()) block = False if len(self.subscribed_events[jid_stripped]) > 5: - if time.time() - self.subscribed_events[jid_stripped][0] < 5: + if time_time() - self.subscribed_events[jid_stripped][0] < 5: block = True self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:] if block: @@ -1816,9 +1878,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # avatar has been updated self.request_vcard(jid_stripped) if not ptype or ptype == 'unavailable': - if gajim.config.get('log_contact_status_changes') and self.name\ - not in no_log_for and jid_stripped not in no_log_for: - gajim.logger.write('status', jid_stripped, status, show) + if gajim.config.get('log_contact_status_changes') and self.name \ + not in no_log_for and jid_stripped not in no_log_for: + try: + gajim.logger.write('status', jid_stripped, status, show) + except exceptions.PysqliteOperationalError, e: + self.dispatch('ERROR', (_('Disk Write Error'), str(e))) self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio, keyID, timestamp, contact_nickname)) # END presenceCB @@ -1875,6 +1940,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, if not self.connection: return self.connection.getRoster(self._on_roster_set) + self.discoverItems(gajim.config.get_per('accounts', self.name, + 'hostname'), id_prefix='p') + self.discoverInfo(gajim.config.get_per('accounts', self.name, + 'hostname'), id_prefix='p') if gajim.config.get_per('accounts', self.name, 'use_ft_proxies'): self.discover_ft_proxies() @@ -1885,10 +1954,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, proxies = map(lambda e:e.strip(), cfg_proxies.split(',')) for proxy in proxies: gajim.proxy65_manager.resolve(proxy, self.connection) - self.discoverItems(gajim.config.get_per('accounts', self.name, - 'hostname'), id_prefix='p') - self.discoverInfo(gajim.config.get_per('accounts', self.name, - 'hostname'), id_prefix='p') def _on_roster_set(self, roster): raw_roster = roster.getRaw() @@ -1951,29 +2016,31 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # Get annotations from private namespace self.get_annotations() - # If it's a gmail account, - # inform the server that we want e-mail notifications - if gajim.get_server_from_jid(our_jid) in gajim.gmail_domains: - gajim.log.debug(('%s is a gmail account. Setting option ' - 'to get e-mail notifications on the server.') % (our_jid)) - iq = common.xmpp.Iq(typ = 'set', to = our_jid) - iq.setAttr('id', 'MailNotify') - query = iq.setTag('usersetting') - query.setNamespace(common.xmpp.NS_GTALKSETTING) - query = query.setTag('mailnotifications') - query.setAttr('value', 'true') - self.connection.send(iq) - # Ask how many messages there are now - iq = common.xmpp.Iq(typ = 'get') - iq.setAttr('id', '13') - query = iq.setTag('query') - query.setNamespace(common.xmpp.NS_GMAILNOTIFY) - self.connection.send(iq) - #Inform GUI we just signed in self.dispatch('SIGNED_IN', ()) self.continue_connect_info = None + def request_gmail_notifications(self): + # It's a gmail account, + # inform the server that we want e-mail notifications + our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) + gajim.log.debug(('%s is a gmail account. Setting option ' + 'to get e-mail notifications on the server.') % (our_jid)) + iq = common.xmpp.Iq(typ = 'set', to = our_jid) + iq.setAttr('id', 'MailNotify') + query = iq.setTag('usersetting') + query.setNamespace(common.xmpp.NS_GTALKSETTING) + query = query.setTag('mailnotifications') + query.setAttr('value', 'true') + self.connection.send(iq) + # Ask how many messages there are now + iq = common.xmpp.Iq(typ = 'get') + iq.setID(self.connection.getAnID()) + query = iq.setTag('query') + query.setNamespace(common.xmpp.NS_GMAILNOTIFY) + self.connection.send(iq) + + def _search_fields_received(self, con, iq_obj): jid = jid = helpers.get_jid_from_iq(iq_obj) tag = iq_obj.getTag('query', namespace = common.xmpp.NS_SEARCH) @@ -1999,6 +2066,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, # that defines handlers con.RegisterHandler('message', self._messageCB) con.RegisterHandler('presence', self._presenceCB) + con.RegisterHandler('presence', self._capsPresenceCB) con.RegisterHandler('iq', self._vCardCB, 'result', common.xmpp.NS_VCARD) con.RegisterHandler('iq', self._rosterSetCB, 'set', diff --git a/src/common/contacts.py b/src/common/contacts.py index 0147a9fd3..65fe13fb7 100644 --- a/src/common/contacts.py +++ b/src/common/contacts.py @@ -2,6 +2,8 @@ ## ## Copyright (C) 2006 Yann Le Boulanger ## Copyright (C) 2006 Nikos Kouremenos +## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## ## This program is free software; you can redistribute it and/or modify @@ -20,7 +22,7 @@ class Contact: '''Information concerning each contact''' def __init__(self, jid='', name='', groups=[], show='', status='', sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None, - chatstate=None, last_status_time=None, msg_id = None, composing_jep = None): + chatstate=None, last_status_time=None, msg_id = None, composing_xep = None): self.jid = jid self.name = name self.contact_name = '' # nick choosen by contact @@ -37,6 +39,12 @@ class Contact: self.priority = priority self.keyID = keyID + # Capabilities; filled by caps.py/ConnectionCaps object + # every time it gets these from presence stanzas + self.caps_node=None + self.caps_ver=None + self.caps_exts=None + # please read jep-85 http://www.jabber.org/jeps/jep-0085.html # we keep track of jep85 support with the peer by three extra states: # None, False and 'ask' @@ -47,9 +55,9 @@ class Contact: self.our_chatstate = our_chatstate self.msg_id = msg_id # tell which JEP we're using for composing state - # None = have to ask, JEP-0022 = use this jep, - # JEP-0085 = use this jep, False = no composing support - self.composing_jep = composing_jep + # None = have to ask, XEP-0022 = use this jep, + # XEP-0085 = use this jep, False = no composing support + self.composing_xep = composing_xep # this is contact's chatstate self.chatstate = chatstate self.last_status_time = last_status_time @@ -153,10 +161,10 @@ class Contacts: def create_contact(self, jid='', name='', groups=[], show='', status='', sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None, - chatstate=None, last_status_time=None, composing_jep=None): + chatstate=None, last_status_time=None, composing_xep=None): return Contact(jid, name, groups, show, status, sub, ask, resource, priority, keyID, our_chatstate, chatstate, last_status_time, - composing_jep) + composing_xep) def copy_contact(self, contact): return self.create_contact(jid = contact.jid, name = contact.name, @@ -208,28 +216,28 @@ class Contacts: # remove metacontacts info self.remove_metacontact(account, jid) - def get_contact(self, account, jid, resource = None): - '''Returns the list of contact instances for this jid (one per resource) - or [] if no resource is given - returns the contact instance for the given resource if it's given - or None if there is not''' + def get_contacts(self, account, jid): + '''Returns the list of contact instances for this jid.''' + if jid in self._contacts[account]: + return self._contacts[account][jid] + else: + return [] + + def get_contact(self, account, jid, resource = None): + '''Returns the contact instance for the given resource if it's given else + the first contact is no resource is given or None if there is not''' if jid in self._contacts[account]: - contacts = self._contacts[account][jid] if not resource: - return contacts - for c in contacts: + return self._contacts[account][jid][0] + for c in self._contacts[account][jid]: if c.resource == resource: return c - if resource: - return None - return [] + return None - def get_contacts_from_jid(self, account, jid): - '''we may have two or more resources on that jid''' - if jid in self._contacts[account]: - contacts_instances = self._contacts[account][jid] - return contacts_instances - return [] + def get_contact_from_full_jid(self, account, fjid): + ''' Get Contact object for specific resource of given jid''' + barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid) + return self.get_contact(account, barejid, resource) def get_highest_prio_contact_from_contacts(self, contacts): if not contacts: @@ -241,7 +249,7 @@ class Contacts: return prim_contact def get_contact_with_highest_priority(self, account, jid): - contacts = self.get_contacts_from_jid(account, jid) + contacts = self.get_contacts(account, jid) if not contacts and '/' in jid: # jid may be a fake jid, try it room, nick = jid.split('/', 1) @@ -258,7 +266,7 @@ class Contacts: '''Returns all contacts in the given group''' group_contacts = [] for jid in self._contacts[account]: - contacts = self.get_contacts_from_jid(account, jid) + contacts = self.get_contacts(account, jid) if group in contacts[0].groups: group_contacts += contacts return group_contacts diff --git a/src/common/dataforms.py b/src/common/dataforms.py index 6c6817d6a..e46fcbfc2 100644 --- a/src/common/dataforms.py +++ b/src/common/dataforms.py @@ -13,7 +13,7 @@ class WrongFieldValue(Error): pass # when we get xmpp.Node which contains bad fi class ExtendedNode(xmpp.Node, object): @classmethod def __new__(cls, *a, **b): - if 'extend' not in b.keys(): + if 'extend' not in b.keys() or not b['extend']: return object.__new__(cls) extend = b['extend'] @@ -76,7 +76,9 @@ def ExtendForm(node): return SimpleDataForm(extend=node) class DataField(ExtendedNode): - """ Keeps data about one field - var, field type, labels, instructions... """ + """ Keeps data about one field - var, field type, labels, instructions... + Base class for different kinds of fields. Use Field() function to + construct one of these. """ def __init__(self, typ=None, var=None, value=None, label=None, desc=None, required=False, options=None, extend=None): @@ -390,8 +392,6 @@ class MultipleDataForm(DataForm): DataForm.__init__(self, type=type, title=title, instructions=instructions, extend=extend) # all records, recorded into DataRecords if extend is None: - # we have to build this object from scratch - xmpp.Node.__init__(self) if items is not None: self.items = items else: diff --git a/src/common/dbus_support.py b/src/common/dbus_support.py index 5a8be077b..94b973c5d 100644 --- a/src/common/dbus_support.py +++ b/src/common/dbus_support.py @@ -60,6 +60,8 @@ class SystemBus: return False if self.system_bus is None: return False + # Don't exit Gajim when dbus is stopped + self.system_bus.set_exit_on_disconnect(False) return True system_bus = SystemBus() diff --git a/src/common/defs.py b/src/common/defs.py index fe72a8e98..d557e6912 100644 --- a/src/common/defs.py +++ b/src/common/defs.py @@ -2,7 +2,7 @@ docdir = '../' datadir = '../' -version = '0.11.1.0' +version = '0.11.1.5' import sys, os.path for base in ('.', 'common'): diff --git a/src/common/events.py b/src/common/events.py index d796cfefb..ddb7eef2e 100644 --- a/src/common/events.py +++ b/src/common/events.py @@ -143,8 +143,8 @@ class Events: self.fire_event_removed(removed_list) return # no event nor type given, remove them all - del self._events[account][jid] self.fire_event_removed(self._events[account][jid]) + del self._events[account][jid] def change_jid(self, account, old_jid, new_jid): if not self._events[account].has_key(old_jid): diff --git a/src/common/exceptions.py b/src/common/exceptions.py index bceef79a4..12d674535 100644 --- a/src/common/exceptions.py +++ b/src/common/exceptions.py @@ -21,6 +21,15 @@ class PysqliteNotAvailable(Exception): def __str__(self): return _('pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting...') +class PysqliteOperationalError(Exception): + '''sqlite2 raised pysqlite2.dbapi2.OperationalError''' + def __init__(self, text=''): + Exception.__init__(self) + self.text = text + + def __str__(self): + return self.text + class ServiceNotAvailable(Exception): '''This exception is raised when we cannot use Gajim remotely''' def __init__(self): diff --git a/src/common/gajim.py b/src/common/gajim.py index 1b2225284..09d837c4d 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -71,6 +71,7 @@ LOGPATH = gajimpaths['LOG'] # deprecated VCARD_PATH = gajimpaths['VCARD'] AVATAR_PATH = gajimpaths['AVATAR'] MY_EMOTS_PATH = gajimpaths['MY_EMOTS'] +MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS'] TMP = gajimpaths['TMP'] DATA_DIR = gajimpaths['DATA'] HOME_DIR = gajimpaths['HOME'] diff --git a/src/common/helpers.py b/src/common/helpers.py index 12388bf81..c768d10f8 100644 --- a/src/common/helpers.py +++ b/src/common/helpers.py @@ -5,6 +5,7 @@ ## Copyright (C) 2005 ## Dimitur Kirov ## Travis Shirk +## Copyright (C) 2007 Lukas Petrovicky ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -206,6 +207,8 @@ def get_contact_dict_for_account(account): contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1 contacts_dict['%s (%s)' % (name, jid)] = contact else: + if contact.name == gajim.get_nick_from_jid(jid): + del contacts_dict[jid] contacts_dict[name] = contact return contacts_dict @@ -462,7 +465,7 @@ def play_sound(event): def play_sound_file(path_to_soundfile): if path_to_soundfile == 'beep': - print '\a' # make a speaker beep + exec_command('beep') return if path_to_soundfile is None or not os.path.exists(path_to_soundfile): return @@ -544,14 +547,19 @@ def get_global_status(): def get_icon_name_to_show(contact, account = None): '''Get the icon name to show in online, away, requested, ...''' if account and gajim.events.get_nb_roster_events(account, contact.jid): - return 'message' + return 'event' if account and gajim.events.get_nb_roster_events(account, contact.get_full_jid()): - return 'message' + return 'event' if account and gajim.interface.minimized_controls.has_key(account) and \ contact.jid in gajim.interface.minimized_controls[account] and gajim.interface.\ minimized_controls[account][contact.jid].get_nb_unread_pm() > 0: - return 'message' + return 'event' + if account and gajim.gc_connected[account].has_key(contact.jid): + if gajim.gc_connected[account][contact.jid]: + return 'muc_active' + else: + return 'muc_inactive' if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent return contact.show if contact.sub in ('both', 'to'): @@ -863,66 +871,127 @@ def reduce_chars_newlines(text, max_chars = 0, max_lines = 0): lines = map(lambda e: _cut_if_long(e), lines) if lines: reduced_text = reduce(lambda e, e1: e + '\n' + e1, lines) + if reduced_text != text: + reduced_text += '...' else: reduced_text = '' return reduced_text -def get_notification_icon_tooltip_text(): - text = None - unread_chat = gajim.events.get_nb_events(types = ['printed_chat', - 'chat']) - unread_single_chat = gajim.events.get_nb_events(types = ['normal']) - unread_gc = gajim.events.get_nb_events(types = ['printed_gc_msg', - 'printed_marked_gc_msg', 'gc_msg']) - unread_pm = gajim.events.get_nb_events(types = ['printed_pm', 'pm']) +def get_account_status(account): + status = reduce_chars_newlines(account['status_line'], 100, 1) + return status + +def get_notification_icon_tooltip_dict(): + '''returns a dict of the form {acct: {'show': show, 'message': message, + 'event_lines': [list of text lines to show in tooltip]}''' + # How many events must there be before they're shown summarized, not per-user + max_ungrouped_events = 10 accounts = get_accounts_info() - if unread_chat or unread_single_chat or unread_gc or unread_pm: - text = 'Gajim ' - awaiting_events = unread_chat + unread_single_chat + unread_gc + unread_pm - if awaiting_events == unread_chat or awaiting_events == unread_single_chat \ - or awaiting_events == unread_gc or awaiting_events == unread_pm: - # This condition is like previous if but with xor... - # Print in one line - text += '-' - else: - # Print in multiple lines - text += '\n ' - if unread_chat: - text += ngettext( - ' %d unread message', - ' %d unread messages', - unread_chat, unread_chat, unread_chat) - text += '\n ' - if unread_single_chat: - text += ngettext( - ' %d unread single message', - ' %d unread single messages', - unread_single_chat, unread_single_chat, unread_single_chat) - text += '\n ' - if unread_gc: - text += ngettext( - ' %d unread group chat message', - ' %d unread group chat messages', - unread_gc, unread_gc, unread_gc) - text += '\n ' - if unread_pm: - text += ngettext( - ' %d unread private message', - ' %d unread private messages', - unread_pm, unread_pm, unread_pm) - text += '\n ' - text = text[:-4] # remove latest '\n ' - elif len(accounts) > 1: - text = _('Gajim') - elif len(accounts) == 1: - message = accounts[0]['status_line'] - message = reduce_chars_newlines(message, 100, 1) - text = _('Gajim - %s') % message - else: - text = _('Gajim - %s') % get_uf_show('offline') + # Gather events. (With accounts, when there are more.) + for account in accounts: + account_name = account['name'] + account['event_lines'] = [] + # Gather events per-account + pending_events = gajim.events.get_events(account = account_name) + messages, non_messages, total_messages, total_non_messages = {}, {}, 0, 0 + for jid in pending_events: + for event in pending_events[jid]: + if event.type_.count('file') > 0: + # This is a non-messagee event. + messages[jid] = non_messages.get(jid, 0) + 1 + total_non_messages = total_non_messages + 1 + else: + # This is a message. + messages[jid] = messages.get(jid, 0) + 1 + total_messages = total_messages + 1 + # Display unread messages numbers, if any + if total_messages > 0: + if total_messages > max_ungrouped_events: + text = ngettext( + '%d message pending', + '%d messages pending', + total_messages, total_messages, total_messages) + account['event_lines'].append(text) + else: + for jid in messages.keys(): + text = ngettext( + '%d message pending', + '%d messages pending', + messages[jid], messages[jid], messages[jid]) + contact = gajim.contacts.get_first_contact_from_jid( + account['name'], jid) + if jid in gajim.gc_connected[account['name']]: + text += _(' from room %s') % (jid) + elif contact: + name = contact.get_shown_name() + text += _(' from user %s') % (name) + else: + text += _(' from %s') % (jid) + account['event_lines'].append(text) + # Display unseen events numbers, if any + if total_non_messages > 0: + if total_non_messages > max_ungrouped_events: + text = ngettext( + '%d event pending', + '%d events pending', + total_non_messages, total_non_messages, total_non_messages) + accounts[account]['event_lines'].append(text) + else: + for jid in non_messages.keys(): + text = ngettext( + '%d event pending', + '%d events pending', + non_messages[jid], non_messages[jid], non_messages[jid]) + text += _(' from user %s') % (jid) + accounts[account]['event_lines'].append(text) + + return accounts + +def get_notification_icon_tooltip_text(): + text = None + # How many events must there be before they're shown summarized, not per-user + max_ungrouped_events = 10 + # Character which should be used to indent in the tooltip. + indent_with = ' ' + + accounts = get_notification_icon_tooltip_dict() + + if len(accounts) == 0: + # No configured account + return _('Gajim') + + # at least one account present + + # Is there more that one account? + if len(accounts) == 1: + show_more_accounts = False + else: + show_more_accounts = True + + # If there is only one account, its status is shown on the first line. + if show_more_accounts: + text = _('Gajim') + else: + text = _('Gajim - %s') % (get_account_status(accounts[0])) + + # Gather and display events. (With accounts, when there are more.) + for account in accounts: + account_name = account['name'] + # Set account status, if not set above + if (show_more_accounts): + message = '\n' + indent_with + ' %s - %s' + text += message % (account_name, get_account_status(account)) + # Account list shown, messages need to be indented more + indent_how = 2 + else: + # If no account list is shown, messages could have default indenting. + indent_how = 1 + for line in account['event_lines']: + text += '\n' + indent_with * indent_how + ' ' + text += line return text def get_accounts_info(): @@ -963,3 +1032,21 @@ def get_avatar_path(prefix): if os.path.exists(file_): return file_ return None + +def datetime_tuple(timestamp): + '''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S + Because of various datetime formats are used the following exceptions + are handled: + - Optional milliseconds appened to the string are removed + - XEP-082 datetime strings have all '-' cahrs removed to meet + the above format.''' + timestamp = timestamp.split('.')[0] + timestamp = timestamp.replace('-', '') + from time import strptime + return strptime(timestamp, '%Y%m%dT%H:%M:%S') + +def get_iconset_path(iconset): + if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', iconset)): + return os.path.join(gajim.DATA_DIR, 'iconsets', iconset) + elif os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, iconset)): + return os.path.join(gajim.MY_ICONSETS_PATH, iconset) diff --git a/src/common/logger.py b/src/common/logger.py index 19c816f64..e5a36a8fe 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -13,10 +13,14 @@ ## GNU General Public License for more details. ## +''' This module allows to access the on-disk database of logs. ''' + import os import sys import time import datetime +from gzip import GzipFile +from cStringIO import StringIO import exceptions import gajim @@ -169,19 +173,21 @@ class Logger: jid = jid.split('/', 1)[0] # remove the resource if jid in self.jids_already_in: # we already have jids in DB self.cur.execute('SELECT jid_id FROM jids WHERE jid=?', [jid]) - jid_id = self.cur.fetchone()[0] - else: # oh! a new jid :), we add it now - if typestr == 'ROOM': - typ = constants.JID_ROOM_TYPE - else: - typ = constants.JID_NORMAL_TYPE - self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid, typ)) - try: - self.con.commit() - except sqlite.OperationalError, e: - print >> sys.stderr, str(e) - jid_id = self.cur.lastrowid - self.jids_already_in.append(jid) + row = self.cur.fetchone() + if row: + return row[0] + # oh! a new jid :), we add it now + if typestr == 'ROOM': + typ = constants.JID_ROOM_TYPE + else: + typ = constants.JID_NORMAL_TYPE + self.cur.execute('INSERT INTO jids (jid, type) VALUES (?, ?)', (jid, typ)) + try: + self.con.commit() + except sqlite.OperationalError, e: + print >> sys.stderr, str(e) + jid_id = self.cur.lastrowid + self.jids_already_in.append(jid) return jid_id def convert_human_values_to_db_api_values(self, kind, show): @@ -285,7 +291,10 @@ class Logger: def commit_to_db(self, values, write_unread = False): #print 'saving', values sql = 'INSERT INTO logs (jid_id, contact_name, time, kind, show, message, subject) VALUES (?, ?, ?, ?, ?, ?, ?)' - self.cur.execute(sql, values) + try: + self.cur.execute(sql, values) + except sqlite.OperationalError, e: + raise exceptions.PysqliteOperationalError(str(e)) message_id = None try: self.con.commit() @@ -625,3 +634,77 @@ class Logger: for result in results: answer[result[0]] = self.convert_api_values_to_human_transport_type(result[1]) return answer + + # A longer note here: + # The database contains a blob field. Pysqlite seems to need special care for such fields. + # When storing, we need to convert string into buffer object (1). + # When retrieving, we need to convert it back to a string to decompress it. (2) + # GzipFile needs a file-like object, StringIO emulates file for plain strings. + def iter_caps_data(self): + ''' Iterate over caps cache data stored in the database. + The iterator values are pairs of (node, ver, ext, identities, features): + identities == {'category':'foo', 'type':'bar', 'name':'boo'}, + features being a list of feature namespaces. ''' + + # get data from table + # the data field contains binary object (gzipped data), this is a hack + # to get that data without trying to convert it to unicode + #tmp, self.con.text_factory = self.con.text_factory, str + try: + self.cur.execute('''SELECT node, ver, ext, data FROM caps_cache;'''); + except sqlite.OperationalError: + # might happen when there's no caps_cache table yet + # -- there's no data to read anyway then + #self.con.text_factory = tmp + return + #self.con.text_factory = tmp + + for node, ver, ext, data in self.cur: + # for each row: unpack the data field + # (format: (category, type, name, category, type, name, ... + # ..., 'FEAT', feature1, feature2, ...).join(' ')) + # NOTE: if there's a need to do more gzip, put that to a function + data=GzipFile(fileobj=StringIO(str(data))).read().split('\0') # (2) -- note above + i=0 + identities=set() + features=set() + while i<(len(data)-2) and data[i]!='FEAT': + category=data[i] + type=data[i+1] + name=data[i+2] + identities.add((category,type,name)) + i+=3 + i+=1 + while i> sys.stderr, str(e) diff --git a/src/common/optparser.py b/src/common/optparser.py index 4ae169df1..c5aaede3e 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -153,9 +153,21 @@ class OptionsParser: self.update_config_to_01101() if old < [0, 11, 0, 2] and new >= [0, 11, 0, 2]: self.update_config_to_01102() + if old < [0, 11, 1, 1] and new >= [0, 11, 1, 1]: + self.update_config_to_01111() + if old < [0, 11, 1, 2] and new >= [0, 11, 1, 2]: + self.update_config_to_01112() + if old < [0, 11, 1, 3] and new >= [0, 11, 1, 3]: + self.update_config_to_01113() + if old < [0, 11, 1, 4] and new >= [0, 11, 1, 4]: + self.update_config_to_01114() + if old < [0, 11, 1, 5] and new >= [0, 11, 1, 5]: + self.update_config_to_01115() gajim.logger.init_vars() gajim.config.set('version', new_version) + + gajim.capscache.load_from_db() def update_config_x_to_09(self): # Var name that changed: @@ -178,7 +190,7 @@ class OptionsParser: 'groupfontattrs', 'contacttextcolor', 'contactbgcolor', 'contactfont', 'contactfontattrs', 'bannertextcolor', 'bannerbgcolor', 'bannerfont', 'bannerfontattrs'] - for theme_name in (_('grocery'), _('gtk+')): + for theme_name in (_('grocery'), _('default')): if theme_name not in gajim.config.get_per('themes'): gajim.config.add_per('themes', theme_name) theme = gajim.config.themes_default[theme_name] @@ -209,7 +221,6 @@ class OptionsParser: def assert_unread_msgs_table_exists(self): '''create table unread_messages if there is no such table''' - #FIXME see #2812 back = os.getcwd() os.chdir(logger.LOG_DB_FOLDER) con = sqlite.connect(logger.LOG_DB_FILE) @@ -384,3 +395,84 @@ class OptionsParser: gajim.config.set('ft_add_hosts_to_send', self.old_values['ft_override_host_to_send']) gajim.config.set('version', '0.11.0.2') + + def update_config_to_01111(self): + '''always_hide_chatbuttons -> compact_view''' + if self.old_values.has_key('always_hide_groupchat_buttons') and \ + self.old_values.has_key('always_hide_chat_buttons'): + gajim.config.set('compact_view', self.old_values['always_hide_groupchat_buttons'] and \ + self.old_values['always_hide_chat_buttons']) + gajim.config.set('version', '0.11.1.1') + + def update_config_to_01112(self): + '''gtk+ theme is renamed to default''' + if self.old_values.has_key('roster_theme') and \ + self.old_values['roster_theme'] == 'gtk+': + gajim.config.set('roster_theme', _('default')) + gajim.config.set('version', '0.11.1.2') + + def update_config_to_01113(self): + # copy&pasted from update_config_to_01013, possibly 'FIXME see #2812' applies too + back = os.getcwd() + os.chdir(logger.LOG_DB_FOLDER) + con = sqlite.connect(logger.LOG_DB_FILE) + os.chdir(back) + cur = con.cursor() + try: + cur.executescript( + ''' + CREATE TABLE caps_cache ( + node TEXT, + ver TEXT, + ext TEXT, + data BLOB + ); + ''' + ) + con.commit() + except sqlite.OperationalError, e: + pass + con.close() + gajim.config.set('version', '0.11.1.3') + + def update_config_to_01114(self): + # add default theme if it doesn't exist + d = ['accounttextcolor', 'accountbgcolor', 'accountfont', + 'accountfontattrs', 'grouptextcolor', 'groupbgcolor', 'groupfont', + 'groupfontattrs', 'contacttextcolor', 'contactbgcolor', 'contactfont', + 'contactfontattrs', 'bannertextcolor', 'bannerbgcolor', 'bannerfont', + 'bannerfontattrs'] + theme_name = _('default') + if theme_name not in gajim.config.get_per('themes'): + gajim.config.add_per('themes', theme_name) + if gajim.config.get_per('themes', 'gtk+'): + # copy from old gtk+ theme + for o in d: + val = gajim.config.get_per('themes', 'gtk+', o) + gajim.config.set_per('themes', theme_name, o, val) + gajim.config.del_per('themes', 'gtk+') + else: + # copy from default theme + theme = gajim.config.themes_default[theme_name] + for o in d: + gajim.config.set_per('themes', theme_name, o, theme[d.index(o)]) + gajim.config.set('version', '0.11.1.4') + + def update_config_to_01115(self): + # copy&pasted from update_config_to_01013, possibly 'FIXME see #2812' applies too + back = os.getcwd() + os.chdir(logger.LOG_DB_FOLDER) + con = sqlite.connect(logger.LOG_DB_FILE) + os.chdir(back) + cur = con.cursor() + try: + cur.executescript( + ''' + DELETE FROM caps_cache; + ''' + ) + con.commit() + except sqlite.OperationalError, e: + pass + con.close() + gajim.config.set('version', '0.11.1.5') diff --git a/src/common/passwords.py b/src/common/passwords.py index 77ddbf8c9..79d83cb16 100644 --- a/src/common/passwords.py +++ b/src/common/passwords.py @@ -81,12 +81,18 @@ class GnomePasswordStorage(PasswordStorage): def save_password(self, account_name, password, update=True): display_name = _('Gajim account %s') % account_name attributes = dict(account_name=str(account_name), gajim=1) - auth_token = gnomekeyring.item_create_sync( - self.keyring, gnomekeyring.ITEM_GENERIC_SECRET, - display_name, attributes, password, update) + try: + auth_token = gnomekeyring.item_create_sync( + self.keyring, gnomekeyring.ITEM_GENERIC_SECRET, + display_name, attributes, password, update) + except gnomekeyring.DeniedError: + set_storage(SimplePasswordStorage()) + storage.save_password(account_name, password) + return token = 'gnomekeyring:%i' % auth_token gajim.config.set_per('accounts', account_name, 'password', token) - + if gajim.connections.has_key(account_name): + gajim.connections[account_name].password = password storage = None def get_storage(): @@ -111,6 +117,8 @@ def get_storage(): storage = GnomePasswordStorage() except gnomekeyring.NoKeyringDaemonError: storage = SimplePasswordStorage() + except gnomekeyring.DeniedError: + storage = SimplePasswordStorage() else: storage = SimplePasswordStorage() return storage diff --git a/src/common/pubsub.py b/src/common/pubsub.py index acf3345dc..1cead4d4c 100644 --- a/src/common/pubsub.py +++ b/src/common/pubsub.py @@ -92,7 +92,7 @@ class ConnectionPubSub: try: cb, args, kwargs = self.__callbacks.pop(stanza.getID()) cb(conn, stanza, *args, **kwargs) - except KeyError: + except: pass def request_pb_configuration(self, jid, node): diff --git a/src/common/socks5.py b/src/common/socks5.py index 80c9a9a67..a4529b781 100644 --- a/src/common/socks5.py +++ b/src/common/socks5.py @@ -348,9 +348,10 @@ class Socks5: def __init__(self, idlequeue, host, port, initiator, target, sid): if host is not None: try: - self.host = socket.gethostbyname(host) + self.host = host + self.ais = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM) except socket.gaierror: - self.host = None + self.ais = None self.idlequeue = idlequeue self.fd = -1 self.port = port @@ -793,6 +794,8 @@ class Socks5Listener(IdleObject): only pollin events though ''' self.port = port + self.ais = socket.getaddrinfo(None, port, socket.AF_UNSPEC, + socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_PASSIVE) self.queue_idx = -1 self.idlequeue = idlequeue self.queue = None @@ -801,14 +804,21 @@ class Socks5Listener(IdleObject): self.fd = -1 def bind(self): - self._serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - # will fail when port as busy, or we don't have rights to bind - try: - self._serv.bind(('0.0.0.0', self.port)) - except Exception, e: + for ai in self.ais: + #try the different possibilities (ipv6, ipv4, etc.) + self._serv = socket.socket(*ai[:3]) + self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + # will fail when port as busy, or we don't have rights to bind + try: + self._serv.bind(ai[4]) + self.ai = ai + break + except: + self.ai = None + continue + if not self.ai: # unable to bind, show error dialog return None self._serv.listen(socket.SOMAXCONN) @@ -884,9 +894,18 @@ class Socks5Receiver(Socks5, IdleObject): def connect(self): ''' create the socket and plug it to the idlequeue ''' - self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # this will not block the GUI - self._sock.setblocking(False) + for ai in self.ais: + try: + self._sock=socket.socket(*ai[:3]) + # this will not block the GUI + self._sock.setblocking(False) + self._server=ai[4] + break + except: + if sys.exc_value[0] == errno.EINPROGRESS: + break + #for all errors, we try other addresses + continue self.fd = self._sock.fileno() self.state = 0 # about to be connected self.idlequeue.plug_idle(self, True, False) @@ -950,7 +969,7 @@ class Socks5Receiver(Socks5, IdleObject): def do_connect(self): try: - self._sock.connect((self.host, self.port)) + self._sock.connect(self._server) self._sock.setblocking(False) self._send=self._sock.send self._recv=self._sock.recv diff --git a/src/common/xmpp/commands.py b/src/common/xmpp/commands.py index 6b23cbfe3..974d0162d 100644 --- a/src/common/xmpp/commands.py +++ b/src/common/xmpp/commands.py @@ -87,7 +87,7 @@ class Commands(PlugIn): elif self._handlers[''].has_key(node): self._handlers[''][node]['execute'](conn,request) else: - conn.send(Error(requet,ERR_ITEM_NOT_FOUND)) + conn.send(Error(request,ERR_ITEM_NOT_FOUND)) raise NodeProcessed def _DiscoHandler(self,conn,request,typ): diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py index 9b3cf342c..d2ae71dfc 100644 --- a/src/common/xmpp/dispatcher_nb.py +++ b/src/common/xmpp/dispatcher_nb.py @@ -23,7 +23,7 @@ Contains one tunable attribute: DefaultTimeout (25 seconds by default). It defin Dispatcher.SendAndWaitForResponce method will wait for reply stanza before giving up. ''' -import simplexml, sys +import simplexml, sys, locale from xml.parsers.expat import ExpatError from protocol import * from client import PlugIn @@ -111,6 +111,9 @@ class Dispatcher(PlugIn): self._metastream.setAttr('version', '1.0') self._metastream.setAttr('xmlns:stream', NS_STREAMS) self._metastream.setAttr('to', self._owner.Server) + if locale.getdefaultlocale()[0]: + self._metastream.setAttr('xml:lang', + locale.getdefaultlocale()[0].split('_')[0]) self._owner.send("%s>" % str(self._metastream)[:-2]) def _check_stream_start(self, ns, tag, attrs): diff --git a/src/common/xmpp/features_nb.py b/src/common/xmpp/features_nb.py index 5785a3034..786c9f6bd 100644 --- a/src/common/xmpp/features_nb.py +++ b/src/common/xmpp/features_nb.py @@ -1,6 +1,7 @@ ## features.py ## ## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -71,6 +72,7 @@ def discoverItems(disp,jid,node=None, cb=None): cb(ret) _discover(disp, NS_DISCO_ITEMS, jid, node, _on_response) +# this one is def discoverInfo(disp,jid,node=None, cb=None): """ Query remote object about info that it publishes. Returns identities and features lists.""" """ According to JEP-0030: @@ -128,16 +130,13 @@ def _ReceivedRegInfo(con, resp, agent): return df=tag.getTag('x',namespace=NS_DATA) if df: - con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,DataForm(node=df),True,'')) + con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,df,True,'')) return - df=DataForm(typ='form') + df={} for i in resp.getQueryPayload(): if not isinstance(i, Node): - pass - elif i.getName()=='instructions': - df.addInstructions(i.getData()) - else: - df.setField(i.getName()).setValue(i.getData()) + continue + df[i.getName()] = i.getData() con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,'')) def register(disp, host, info, cb): diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index ead233682..3c160a62c 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -21,8 +21,8 @@ xmpp-related data structures. from simplexml import Node,NodeBuilder,ustr import time -NS_ACTIVITY ='http://jabber.org/protocol/activity' # JEP-0108 -NS_ADDRESS ='http://jabber.org/protocol/address' # JEP-0033 +NS_ACTIVITY ='http://jabber.org/protocol/activity' # XEP-0108 +NS_ADDRESS ='http://jabber.org/protocol/address' # XEP-0033 NS_AGENTS ='jabber:iq:agents' NS_AMP ='http://jabber.org/protocol/amp' NS_AMP_ERRORS =NS_AMP+'#errors' @@ -39,58 +39,58 @@ NS_CLIENT ='jabber:client' NS_COMMANDS ='http://jabber.org/protocol/commands' NS_COMPONENT_ACCEPT='jabber:component:accept' NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0' -NS_COMPRESS ='http://jabber.org/protocol/compress' # JEP-0138 +NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138 NS_CONFERENCE ='jabber:x:conference' -NS_DATA ='jabber:x:data' # JEP-0004 +NS_DATA ='jabber:x:data' # XEP-0004 NS_DELAY ='jabber:x:delay' NS_DIALBACK ='jabber:server:dialback' NS_DISCO ='http://jabber.org/protocol/disco' NS_DISCO_INFO =NS_DISCO+'#info' NS_DISCO_ITEMS =NS_DISCO+'#items' -NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027 -NS_EVENT ='jabber:x:event' # JEP-0022 +NS_ENCRYPTED ='jabber:x:encrypted' # XEP-0027 +NS_EVENT ='jabber:x:event' # XEP-0022 NS_FEATURE ='http://jabber.org/protocol/feature-neg' NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196 NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080 NS_GROUPCHAT ='gc-1.0' -NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070 -NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # JEP-0124 +NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070 +NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124 NS_IBB ='http://jabber.org/protocol/ibb' NS_INVISIBLE ='presence-invisible' # Jabberd2 NS_IQ ='iq' # Jabberd2 NS_LAST ='jabber:iq:last' NS_MESSAGE ='message' # Jabberd2 -NS_MOOD ='http://jabber.org/protocol/mood' # JEP-0107 +NS_MOOD ='http://jabber.org/protocol/mood' # XEP-0107 NS_MUC ='http://jabber.org/protocol/muc' NS_MUC_USER =NS_MUC+'#user' NS_MUC_ADMIN =NS_MUC+'#admin' NS_MUC_OWNER =NS_MUC+'#owner' -NS_NICK ='http://jabber.org/protocol/nick' # JEP-0172 -NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013 -NS_PHYSLOC ='http://jabber.org/protocol/physloc' # JEP-0112 +NS_NICK ='http://jabber.org/protocol/nick' # XEP-0172 +NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # XEP-0013 +NS_PHYSLOC ='http://jabber.org/protocol/physloc' # XEP-0112 NS_PRESENCE ='presence' # Jabberd2 NS_PRIVACY ='jabber:iq:privacy' NS_PRIVATE ='jabber:iq:private' -NS_PROFILE ='http://jabber.org/protocol/profile' # JEP-0154 -NS_PUBSUB ='http://jabber.org/protocol/pubsub' # JEP-0060 +NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154 +NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060 NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060 NS_REGISTER ='jabber:iq:register' NS_ROSTER ='jabber:iq:roster' -NS_ROSTERX ='http://jabber.org/protocol/rosterx' # JEP-0144 -NS_RPC ='jabber:iq:rpc' # JEP-0009 +NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144 +NS_RPC ='jabber:iq:rpc' # XEP-0009 NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl' NS_SEARCH ='jabber:iq:search' NS_SERVER ='jabber:server' NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' -NS_SI ='http://jabber.org/protocol/si' # JEP-0096 -NS_SI_PUB ='http://jabber.org/protocol/sipub' # JEP-0137 -NS_SIGNED ='jabber:x:signed' # JEP-0027 +NS_SI ='http://jabber.org/protocol/si' # XEP-0096 +NS_SI_PUB ='http://jabber.org/protocol/sipub' # XEP-0137 +NS_SIGNED ='jabber:x:signed' # XEP-0027 NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas' NS_STREAM ='http://affinix.com/jabber/stream' NS_STREAMS ='http://etherx.jabber.org/streams' -NS_TIME ='jabber:iq:time' # JEP-0900 -NS_TIME_REVISED ='http://www.xmpp.org/extensions/xep-0202.html#ns' # JEP-0202 +NS_TIME ='jabber:iq:time' # XEP-0900 +NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202 NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118 NS_VACATION ='http://jabber.org/protocol/vacation' @@ -101,11 +101,11 @@ NS_VCARD_UPDATE =NS_VCARD+':x:update' NS_VERSION ='jabber:iq:version' NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197 NS_PING ='urn:xmpp:ping' # XEP-0199 -NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # JEP-0130 -NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # JEP-0071 +NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130 +NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071 NS_XHTML = 'http://www.w3.org/1999/xhtml' # " -NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # JEP-0141 -NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # JEP-0122 +NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141 +NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122 NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' xmpp_stream_error_conditions=""" @@ -429,7 +429,7 @@ class Message(Protocol): self.setTagData('body',val) def setXHTML(self,val,xmllang=None): - """ Sets the xhtml text of the message (JEP-0071). + """ Sets the xhtml text of the message (XEP-0071). The parameter is the "inner html" to the body.""" try: if xmllang: @@ -456,6 +456,14 @@ class Message(Protocol): th=self.getThread() if th: m.setThread(th) return m + def getStatusCode(self): + """Returns the status code of the message (for groupchat config + change)""" + attrs = [] + for xtag in self.getTags('x'): + for child in xtag.getTags('status'): + attrs.append(child.getAttr('code')) + return attrs class Presence(Protocol): """ XMPP Presence object.""" @@ -516,7 +524,11 @@ class Presence(Protocol): return self._muc_getSubTagDataAttr('actor','jid')[1] def getStatusCode(self): """Returns the status code of the presence (for groupchat)""" - return self._muc_getItemAttr('status','code') + attrs = [] + for xtag in self.getTags('x'): + for child in xtag.getTags('status'): + attrs.append(child.getAttr('code')) + return attrs class Iq(Protocol): """ XMPP Iq object - get/set dialog mechanism. """ @@ -597,7 +609,7 @@ class Error(Protocol): class DataField(Node): """ This class is used in the DataForm class to describe the single data item. - If you are working with jabber:x:data (JEP-0004, JEP-0068, JEP-0122) + If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) then you will need to work with instances of this class. """ def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None): """ Create new data field of specified name,value and type. @@ -674,7 +686,7 @@ class DataField(Node): class DataForm(Node): """ DataForm class. Used for manipulating dataforms in XMPP. - Relevant JEPs: 0004, 0068, 0122. + Relevant XEPs: 0004, 0068, 0122. Can be used in disco, pub-sub and many other applications.""" def __init__(self, typ=None, data=[], title=None, node=None): """ diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index c2dfd1924..c5115cc1c 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -33,6 +33,8 @@ import thread import logging log = logging.getLogger('gajim.c.x.transports_nb') +from common import gajim + USE_PYOPENSSL = False try: @@ -296,7 +298,7 @@ class NonBlockingTcp(PlugIn, IdleObject): self.renew_send_timeout() def connect(self,server=None, proxy = None, secure = None): - ''' Try to establish connection. Returns non-empty string on success. ''' + ''' Try to establish connection. Returns True/False on success/failure. ''' if not server: server=self._server else: @@ -416,6 +418,12 @@ class NonBlockingTcp(PlugIn, IdleObject): self.idlequeue.remove_timeout(self.fd) def onreceive(self, recv_handler): + ''' Sets the on_receive callback. Do not confuse it with + on_receive() method, which is the callback itself. + + If recv_handler==None, it tries to set that callback assuming that + our owner also has a Dispatcher object plugged in, to its + ProcessNonBlocking method.''' if not recv_handler: if hasattr(self._owner, 'Dispatcher'): self.on_receive = self._owner.Dispatcher.ProcessNonBlocking @@ -735,7 +743,12 @@ class NonBlockingTLS(PlugIn): # FIXME: should method be configurable? tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) - tcpsock._sslContext.set_info_callback(self._ssl_info_callback) + tcpsock.ssl_errnum = 0 + tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) + try: + tcpsock._sslContext.load_verify_locations(os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem')) + except: + log.warning(_("Unable to load SSL certificats from file %s" % os.path.abspath(os.path.join(gajim.DATA_DIR,'other','ca.crt')))) tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) tcpsock._sslObj.set_connect_state() # set to client mode @@ -759,29 +772,6 @@ class NonBlockingTLS(PlugIn): # fake it, for now self.starttls='success' - def _on_ssl_handshake_done(self): - log.debug("Handshake done!") - #self.starttls='success' - - tcpsock = self._owner.Connection - cert = tcpsock._sslObj.get_peer_certificate() - peer = cert.get_subject() - issuer = cert.get_issuer() - tcpsock._sslIssuer = unicode(issuer) - tcpsock._sslServer = unicode(peer) - tcpsock.serverDigestSHA1 = cert.digest('sha1') - tcpsock.serverDigestMD5 = cert.digest('md5') - - if log.getEffectiveLevel() <= logging.DEBUG: - peercert = tcpsock._sslObj.get_peer_certificate() - ciphers = tcpsock._sslObj.get_cipher_list() - - print >> sys.stderr, "Ciphers:", ciphers - print >> sys.stderr, "Peer cert:", peercert - self._dumpX509(peercert) - - print >> sys.stderr, OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, peercert) - def _startSSL_stdlib(self): log.debug("_startSSL_stdlib called") tcpsock=self._owner.Connection @@ -795,37 +785,17 @@ class NonBlockingTLS(PlugIn): tcpsock._send = wrapper.send self.starttls='success' - def _ssl_info_callback(self, sslconn, type, st): + def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): # Exceptions can't propagate up through this callback, so print them here. try: - self._ssl_info_callback_guarded(sslconn, type, st) + if errnum == 0: + return True + self._owner.Connection.ssl_errnum = errnum + return True except: log.error("Exception caught in _ssl_info_callback:", exc_info=True) traceback.print_exc() # Make sure something is printed, even if log is disabled. - def _ssl_info_callback_guarded(self, sslconn, type, st): - b = self.ssl_h_bits - - #if type & b['SSL_CB_LOOP']: - # if type & SSL_ST_CONNECT: tls_state = "connect" - # elif type & SSL_ST_ACCEPT: tls_state = "accept" - # else: tls_state = "undefined" - # print "tls_state: %s: %s" % (tls_state, sslconn.state_string()) - - #if type & b['SSL_CB_ALERT']: - # if type & SSL_CB_READ: rdwr = "read" - # elif type & SSL_CB_WRITE: rdwr = "write" - # else: rdwr = "unknown" - # print "tls_alert: %s:%d: %s" % (rdwr, st, sslconn.state_string()) - - #mask = "" - #for k, v in b.iteritems(): - # if type & v: mask += " " + k - #print "mask:", mask, st - - if type & b['SSL_CB_HANDSHAKE_DONE']: - self._on_ssl_handshake_done() - def StartTLSHandler(self, conn, starttls): ''' Handle server reply if TLS is allowed to process. Behaves accordingly. Used internally.''' diff --git a/src/common/zeroconf/client_zeroconf.py b/src/common/zeroconf/client_zeroconf.py index e9d9f452d..c3de88a38 100644 --- a/src/common/zeroconf/client_zeroconf.py +++ b/src/common/zeroconf/client_zeroconf.py @@ -121,6 +121,7 @@ class P2PClient(IdleObject): self.sock_type = TYPE_SERVER else: self.sock_type = TYPE_CLIENT + self.fd = -1 conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect, self) self.sock_hash = conn._sock.__hash__ self.fd = conn.fd @@ -129,10 +130,14 @@ class P2PClient(IdleObject): for val in self.stanzaqueue: stanza, is_message = val if is_message: - if self.conn_holder.number_of_awaiting_messages.has_key(self.fd): - self.conn_holder.number_of_awaiting_messages[self.fd]+=1 + if self.fd == -1: + self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \ + _('Connection to host could not be established'), None, None]) else: - self.conn_holder.number_of_awaiting_messages[self.fd]=1 + if self.conn_holder.number_of_awaiting_messages.has_key(self.fd): + self.conn_holder.number_of_awaiting_messages[self.fd]+=1 + else: + self.conn_holder.number_of_awaiting_messages[self.fd]=1 def add_stanza(self, stanza, is_message = False): if self.Connection: @@ -207,11 +212,11 @@ class P2PClient(IdleObject): def on_disconnect(self): if self.conn_holder: - if self.conn_holder.number_of_awaiting_messages.has_key(self.fd): - if self.conn_holder.number_of_awaiting_messages[self.fd] > 0: + if self.conn_holder.number_of_awaiting_messages.has_key(self.conn_holder.fd): + if self.conn_holder.number_of_awaiting_messages[self.conn_holder.fd] > 0: self._caller.dispatch('MSGERROR',[unicode(self.to), -1, \ _('Connection to host could not be established'), None, None]) - del self.conn_holder.number_of_awaiting_messages[self.fd] + del self.conn_holder.number_of_awaiting_messages[self.conn_holder.fd] self.conn_holder.remove_connection(self.sock_hash) if self.__dict__.has_key('Dispatcher'): self.Dispatcher.PlugOut() @@ -518,13 +523,6 @@ class ClientZeroconf: self.listener = None self.number_of_awaiting_messages = {} - def test_avahi(self): - try: - import avahi - except ImportError: - return False - return True - def connect(self, show, msg): self.port = self.start_listener(self.caller.port) if not self.port: diff --git a/src/common/zeroconf/connection_handlers_zeroconf.py b/src/common/zeroconf/connection_handlers_zeroconf.py index 2d36d4661..eaa2f6a17 100644 --- a/src/common/zeroconf/connection_handlers_zeroconf.py +++ b/src/common/zeroconf/connection_handlers_zeroconf.py @@ -640,7 +640,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream): mtype = msg.getType() subject = msg.getSubject() # if not there, it's None tim = msg.getTimestamp() - tim = time.strptime(tim, '%Y%m%dT%H:%M:%S') + tim = helpers.datetime_tuple(tim) tim = time.localtime(timegm(tim)) frm = msg.getFrom() if frm == None: @@ -663,23 +663,23 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream): invite = None delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None msg_id = None - composing_jep = None + composing_xep = None xtags = msg.getTags('x') # chatstates - look for chatstate tags in a message if not delayed if not delayed: - composing_jep = False + composing_xep = False children = msg.getChildren() for child in children: if child.getNamespace() == 'http://jabber.org/protocol/chatstates': chatstate = child.getName() - composing_jep = 'JEP-0085' + composing_xep = 'XEP-0085' break # No JEP-0085 support, fallback to JEP-0022 if not chatstate: chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT) if chatstate_child: chatstate = 'active' - composing_jep = 'JEP-0022' + composing_xep = 'XEP-0022' if not msgtxt and chatstate_child.getTag('composing'): chatstate = 'composing' # JEP-0172 User Nickname @@ -715,14 +715,14 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream): msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim, subject = subject) self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject, - chatstate, msg_id, composing_jep, user_nick, msghtml)) + chatstate, msg_id, composing_xep, user_nick, msghtml)) elif mtype == 'normal': # it's single message if self.name not in no_log_for and jid not in no_log_for and msgtxt: gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim, subject = subject) if invite: self.dispatch('MSG', (frm, msgtxt, tim, encrypted, 'normal', - subject, chatstate, msg_id, composing_jep, user_nick)) + subject, chatstate, msg_id, composing_xep, user_nick)) # END messageCB def parse_data_form(self, node): diff --git a/src/common/zeroconf/connection_zeroconf.py b/src/common/zeroconf/connection_zeroconf.py index 29d40226c..b218eab41 100644 --- a/src/common/zeroconf/connection_zeroconf.py +++ b/src/common/zeroconf/connection_zeroconf.py @@ -41,6 +41,7 @@ import gobject from common import gajim from common import GnuPG from common.zeroconf import client_zeroconf +from common.zeroconf import zeroconf from connection_handlers_zeroconf import * USE_GPG = GnuPG.USE_GPG @@ -103,12 +104,11 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf): gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'password', 'zeroconf') gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status', True) - #XXX make sure host is US-ASCII - self.host = unicode(socket.gethostname()) - gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', self.host) gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port', 5298) gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'is_zeroconf', True) - self.host = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname') + #XXX make sure host is US-ASCII + self.host = unicode(socket.gethostname()) + gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', self.host) self.port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port') self.autoconnect = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'autoconnect') self.sync_with_global_status = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status') @@ -229,7 +229,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf): self.get_config_values_or_default() if not self.connection: self.connection = client_zeroconf.ClientZeroconf(self) - if not self.connection.test_avahi(): + if not zeroconf.test_zeroconf(): self.dispatch('STATUS', 'offline') self.status = 'offline' self.dispatch('CONNECTION_LOST', @@ -348,7 +348,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf): return STATUS_LIST[self.connected] def send_message(self, jid, msg, keyID, type = 'chat', subject='', - chatstate = None, msg_id = None, composing_jep = None, resource = None, + chatstate = None, msg_id = None, composing_xep = None, resource = None, user_nick = None): fjid = jid @@ -396,10 +396,10 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf): # please note that the only valid tag inside a message containing a # tag is the active event if chatstate is not None: - if composing_jep == 'JEP-0085' or not composing_jep: + if composing_xep == 'XEP-0085' or not composing_xep: # JEP-0085 msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES) - if composing_jep == 'JEP-0022' or not composing_jep: + if composing_xep == 'XEP-0022' or not composing_xep: # JEP-0022 chatstate_node = msg_iq.setTag('x', namespace = common.xmpp.NS_EVENT) if not msgtxt: # when no , add diff --git a/src/common/zeroconf/zeroconf.py b/src/common/zeroconf/zeroconf.py old mode 100755 new mode 100644 index 6a65e622d..e34599da2 --- a/src/common/zeroconf/zeroconf.py +++ b/src/common/zeroconf/zeroconf.py @@ -12,404 +12,29 @@ ## GNU General Public License for more details. ## -from common import gajim - -try: - import dbus.glib -except ImportError, e: - pass - - C_NAME, C_DOMAIN, C_INTERFACE, C_PROTOCOL, C_HOST, \ C_ADDRESS, C_PORT, C_BARE_NAME, C_TXT = range(9) -class Zeroconf: - def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB, - disconnected_CB, error_CB, name, host, port): - self.avahi = None - self.domain = None # specific domain to browse - self.stype = '_presence._tcp' - self.port = port # listening port that gets announced - self.username = name - self.host = host - self.txt = {} # service data - - #XXX these CBs should be set to None when we destroy the object - # (go offline), because they create a circular reference - self.new_serviceCB = new_serviceCB - self.remove_serviceCB = remove_serviceCB - self.name_conflictCB = name_conflictCB - self.disconnected_CB = disconnected_CB - self.error_CB = error_CB - - self.service_browser = None - self.domain_browser = None - self.bus = None - self.server = None - self.contacts = {} # all current local contacts with data - self.entrygroup = None - self.connected = False - self.announced = False - self.invalid_self_contact = {} +def test_avahi(): + try: + import avahi + except ImportError: + return False + return True +def test_bonjour(): + try: + import pybonjour + except ImportError: + return False + return True - ## handlers for dbus callbacks - def entrygroup_commit_error_CB(self, err): - # left blank for possible later usage - pass - - def error_callback1(self, err): - gajim.log.debug('Error while resolving: ' + str(err)) - - def error_callback(self, err): - gajim.log.debug(str(err)) - # timeouts are non-critical - if str(err) != 'Timeout reached': - self.disconnect() - self.disconnected_CB() +def test_zeroconf(): + return test_avahi() or test_bonjour() - def new_service_callback(self, interface, protocol, name, stype, domain, flags): - gajim.log.debug('Found service %s in domain %s on %i.%i.' % (name, domain, interface, protocol)) - if not self.connected: - return - - # synchronous resolving - self.server.ResolveService( int(interface), int(protocol), name, stype, \ - domain, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), \ - reply_handler=self.service_resolved_callback, error_handler=self.error_callback1) - - def remove_service_callback(self, interface, protocol, name, stype, domain, flags): - gajim.log.debug('Service %s in domain %s on %i.%i disappeared.' % (name, domain, interface, protocol)) - if not self.connected: - return - if name != self.name: - for key in self.contacts.keys(): - if self.contacts[key][C_BARE_NAME] == name: - del self.contacts[key] - self.remove_serviceCB(key) - return - - def new_service_type(self, interface, protocol, stype, domain, flags): - # Are we already browsing this domain for this type? - if self.service_browser: - return - - object_path = self.server.ServiceBrowserNew(interface, protocol, \ - stype, domain, dbus.UInt32(0)) - - self.service_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ - object_path) , self.avahi.DBUS_INTERFACE_SERVICE_BROWSER) - self.service_browser.connect_to_signal('ItemNew', self.new_service_callback) - self.service_browser.connect_to_signal('ItemRemove', self.remove_service_callback) - self.service_browser.connect_to_signal('Failure', self.error_callback) - - def new_domain_callback(self,interface, protocol, domain, flags): - if domain != "local": - self.browse_domain(interface, protocol, domain) - - def txt_array_to_dict(self, txt_array): - txt_dict = {} - for els in txt_array: - key, val = '', None - for c in els: - #FIXME: remove when outdated, this is for avahi < 0.6.14 - if c < 0 or c > 255: - c = '.' - else: - c = chr(c) - if val is None: - if c == '=': - val = '' - else: - key += c - else: - val += c - if val is None: # missing '=' - val = '' - txt_dict[key] = val.decode('utf-8') - return txt_dict - - def service_resolved_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): - gajim.log.debug('Service data for service %s in domain %s on %i.%i:' - % (name, domain, interface, protocol)) - gajim.log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address, port, - self.txt_array_to_dict(txt))) - if not self.connected: - return - bare_name = name - if name.find('@') == -1: - name = name + '@' + name - - # we don't want to see ourselves in the list - if name != self.name: - self.contacts[name] = (name, domain, interface, protocol, host, address, port, - bare_name, txt) - self.new_serviceCB(name) - else: - # remember data - # In case this is not our own record but of another - # gajim instance on the same machine, - # it will be used when we get a new name. - self.invalid_self_contact[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt) - - - # different handler when resolving all contacts - def service_resolved_all_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): - if not self.connected: - return - bare_name = name - if name.find('@') == -1: - name = name + '@' + name - self.contacts[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt) - - def service_added_callback(self): - gajim.log.debug('Service successfully added') - - def service_committed_callback(self): - gajim.log.debug('Service successfully committed') - - def service_updated_callback(self): - gajim.log.debug('Service successfully updated') - - def service_add_fail_callback(self, err): - gajim.log.debug('Error while adding service. %s' % str(err)) - if str(err) == 'Local name collision': - alternative_name = self.server.GetAlternativeServiceName(self.username) - self.name_conflictCB(alternative_name) - return - self.error_CB(_('Error while adding service. %s') % str(err)) - self.disconnect() - - def server_state_changed_callback(self, state, error): - if state == self.avahi.SERVER_RUNNING: - self.create_service() - elif state == self.avahi.SERVER_COLLISION: - self.entrygroup.Reset() - elif state == self.avahi.CLIENT_FAILURE: - # does it ever go here? - gajim.log.debug('CLIENT FAILURE') - - def entrygroup_state_changed_callback(self, state, error): - # the name is already present, so recreate - if state == self.avahi.ENTRY_GROUP_COLLISION: - self.service_add_fail_callback('Local name collision') - elif state == self.avahi.ENTRY_GROUP_FAILURE: - gajim.log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached(that' - ' should not happen)') - - # make zeroconf-valid names - def replace_show(self, show): - if show in ['chat', 'online', '']: - return 'avail' - elif show == 'xa': - return 'away' - return show - - def avahi_txt(self): - utf8_dict = {} - for key in self.txt: - val = self.txt[key] - if isinstance(val, unicode): - utf8_dict[key] = val.encode('utf-8') - else: - utf8_dict[key] = val - return self.avahi.dict_to_txt_array(utf8_dict) - - def create_service(self): - try: - if not self.entrygroup: - # create an EntryGroup for publishing - self.entrygroup = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, self.server.EntryGroupNew()), self.avahi.DBUS_INTERFACE_ENTRY_GROUP) - self.entrygroup.connect_to_signal('StateChanged', self.entrygroup_state_changed_callback) - - txt = {} - - #remove empty keys - for key,val in self.txt.iteritems(): - if val: - txt[key] = val - - txt['port.p2pj'] = self.port - txt['version'] = 1 - txt['txtvers'] = 1 - - # replace gajim's show messages with compatible ones - if self.txt.has_key('status'): - txt['status'] = self.replace_show(self.txt['status']) - else: - txt['status'] = 'avail' - - self.txt = txt - gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype)) - self.entrygroup.AddService(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '', '', self.port, self.avahi_txt(), reply_handler=self.service_added_callback, error_handler=self.service_add_fail_callback) - - self.entrygroup.Commit(reply_handler=self.service_committed_callback, - error_handler=self.entrygroup_commit_error_CB) - - return True - - except dbus.DBusException, e: - gajim.log.debug(str(e)) - return False - - def announce(self): - if not self.connected: - return False - - state = self.server.GetState() - if state == self.avahi.SERVER_RUNNING: - self.create_service() - self.announced = True - return True - - def remove_announce(self): - if self.announced == False: - return False - try: - if self.entrygroup.GetState() != self.avahi.ENTRY_GROUP_FAILURE: - self.entrygroup.Reset() - self.entrygroup.Free() - # .Free() has mem leaks - obj = self.entrygroup._obj - obj._bus = None - self.entrygroup._obj = None - self.entrygroup = None - self.announced = False - - return True - else: - return False - except dbus.DBusException, e: - gajim.log.debug("Can't remove service. That should not happen") - - def browse_domain(self, interface, protocol, domain): - self.new_service_type(interface, protocol, self.stype, domain, '') - - def avahi_dbus_connect_cb(self, a, connect, disconnect): - if connect != "": - gajim.log.debug('Lost connection to avahi-daemon') - self.disconnect() - if self.disconnected_CB: - self.disconnected_CB() - else: - gajim.log.debug('We are connected to avahi-daemon') - - # connect to dbus - def connect_dbus(self): - try: - import dbus - except ImportError: - gajim.log.debug('Error: python-dbus needs to be installed. No zeroconf support.') - return False - if self.bus: - return True - try: - self.bus = dbus.SystemBus() - self.bus.add_signal_receiver(self.avahi_dbus_connect_cb, - "NameOwnerChanged", "org.freedesktop.DBus", - arg0="org.freedesktop.Avahi") - except Exception, e: - # System bus is not present - self.bus = None - gajim.log.debug(str(e)) - return False - else: - return True - - # connect to avahi - def connect_avahi(self): - if not self.connect_dbus(): - return False - try: - import avahi - self.avahi = avahi - except ImportError: - gajim.log.debug('Error: python-avahi needs to be installed. No zeroconf support.') - return False - - if self.server: - return True - try: - self.server = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ - self.avahi.DBUS_PATH_SERVER), self.avahi.DBUS_INTERFACE_SERVER) - self.server.connect_to_signal('StateChanged', - self.server_state_changed_callback) - except Exception, e: - # Avahi service is not present - self.server = None - gajim.log.debug(str(e)) - return False - else: - return True - - def connect(self): - self.name = self.username + '@' + self.host # service name - if not self.connect_avahi(): - return False - - self.connected = True - # start browsing - if self.domain is None: - # Explicitly browse .local - self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, "local") - - # Browse for other browsable domains - self.domain_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ - self.server.DomainBrowserNew(self.avahi.IF_UNSPEC, \ - self.avahi.PROTO_UNSPEC, '', self.avahi.DOMAIN_BROWSER_BROWSE,\ - dbus.UInt32(0))), self.avahi.DBUS_INTERFACE_DOMAIN_BROWSER) - self.domain_browser.connect_to_signal('ItemNew', self.new_domain_callback) - self.domain_browser.connect_to_signal('Failure', self.error_callback) - else: - self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, self.domain) - - return True - - def disconnect(self): - if self.connected: - self.connected = False - if self.service_browser: - self.service_browser.Free() - self.service_browser._obj._bus = None - self.service_browser._obj = None - if self.domain_browser: - self.domain_browser.Free() - self.domain_browser._obj._bus = None - self.domain_browser._obj = None - self.remove_announce() - self.server._obj._bus = None - self.server._obj = None - self.server = None - self.service_browser = None - self.domain_browser = None - - # refresh txt data of all contacts manually (no callback available) - def resolve_all(self): - if not self.connected: - return - for val in self.contacts.values(): - self.server.ResolveService(int(val[C_INTERFACE]), int(val[C_PROTOCOL]), val[C_BARE_NAME], \ - self.stype, val[C_DOMAIN], self.avahi.PROTO_UNSPEC, dbus.UInt32(0),\ - reply_handler=self.service_resolved_all_callback, error_handler=self.error_callback) - - def get_contacts(self): - return self.contacts - - def get_contact(self, jid): - if not jid in self.contacts: - return None - return self.contacts[jid] - - def update_txt(self, show = None): - if show: - self.txt['status'] = self.replace_show(show) - - txt = self.avahi_txt() - if self.connected and self.entrygroup: - self.entrygroup.UpdateServiceTxt(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype,'', txt, reply_handler=self.service_updated_callback, error_handler=self.error_callback) - return True - else: - return False - - -# END Zeroconf +if test_avahi(): + from common.zeroconf import zeroconf_avahi + Zeroconf = zeroconf_avahi.Zeroconf +elif test_bonjour(): + from common.zeroconf import zeroconf_bonjour + Zeroconf = zeroconf_bonjour.Zeroconf diff --git a/src/common/zeroconf/zeroconf_avahi.py b/src/common/zeroconf/zeroconf_avahi.py new file mode 100644 index 000000000..6ad768826 --- /dev/null +++ b/src/common/zeroconf/zeroconf_avahi.py @@ -0,0 +1,415 @@ +## common/zeroconf/zeroconf.py +## +## Copyright (C) 2006 Stefan Bethge +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 2 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from common import gajim + +try: + import dbus.glib +except ImportError, e: + pass + +from common.zeroconf.zeroconf import C_BARE_NAME, C_INTERFACE, C_PROTOCOL, C_DOMAIN + +class Zeroconf: + def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB, + disconnected_CB, error_CB, name, host, port): + self.avahi = None + self.domain = None # specific domain to browse + self.stype = '_presence._tcp' + self.port = port # listening port that gets announced + self.username = name + self.host = host + self.txt = {} # service data + + #XXX these CBs should be set to None when we destroy the object + # (go offline), because they create a circular reference + self.new_serviceCB = new_serviceCB + self.remove_serviceCB = remove_serviceCB + self.name_conflictCB = name_conflictCB + self.disconnected_CB = disconnected_CB + self.error_CB = error_CB + + self.service_browser = None + self.domain_browser = None + self.bus = None + self.server = None + self.contacts = {} # all current local contacts with data + self.entrygroup = None + self.connected = False + self.announced = False + self.invalid_self_contact = {} + + + ## handlers for dbus callbacks + def entrygroup_commit_error_CB(self, err): + # left blank for possible later usage + pass + + def error_callback1(self, err): + gajim.log.debug('Error while resolving: ' + str(err)) + + def error_callback(self, err): + gajim.log.debug(str(err)) + # timeouts are non-critical + if str(err) != 'Timeout reached': + self.disconnect() + self.disconnected_CB() + + def new_service_callback(self, interface, protocol, name, stype, domain, flags): + gajim.log.debug('Found service %s in domain %s on %i.%i.' % (name, domain, interface, protocol)) + if not self.connected: + return + + # synchronous resolving + self.server.ResolveService( int(interface), int(protocol), name, stype, \ + domain, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), \ + reply_handler=self.service_resolved_callback, error_handler=self.error_callback1) + + def remove_service_callback(self, interface, protocol, name, stype, domain, flags): + gajim.log.debug('Service %s in domain %s on %i.%i disappeared.' % (name, domain, interface, protocol)) + if not self.connected: + return + if name != self.name: + for key in self.contacts.keys(): + if self.contacts[key][C_BARE_NAME] == name: + del self.contacts[key] + self.remove_serviceCB(key) + return + + def new_service_type(self, interface, protocol, stype, domain, flags): + # Are we already browsing this domain for this type? + if self.service_browser: + return + + object_path = self.server.ServiceBrowserNew(interface, protocol, \ + stype, domain, dbus.UInt32(0)) + + self.service_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ + object_path) , self.avahi.DBUS_INTERFACE_SERVICE_BROWSER) + self.service_browser.connect_to_signal('ItemNew', self.new_service_callback) + self.service_browser.connect_to_signal('ItemRemove', self.remove_service_callback) + self.service_browser.connect_to_signal('Failure', self.error_callback) + + def new_domain_callback(self,interface, protocol, domain, flags): + if domain != "local": + self.browse_domain(interface, protocol, domain) + + def txt_array_to_dict(self, txt_array): + txt_dict = {} + for els in txt_array: + key, val = '', None + for c in els: + #FIXME: remove when outdated, this is for avahi < 0.6.14 + if c < 0 or c > 255: + c = '.' + else: + c = chr(c) + if val is None: + if c == '=': + val = '' + else: + key += c + else: + val += c + if val is None: # missing '=' + val = '' + txt_dict[key] = val.decode('utf-8') + return txt_dict + + def service_resolved_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): + gajim.log.debug('Service data for service %s in domain %s on %i.%i:' + % (name, domain, interface, protocol)) + gajim.log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address, port, + self.txt_array_to_dict(txt))) + if not self.connected: + return + bare_name = name + if name.find('@') == -1: + name = name + '@' + name + + # we don't want to see ourselves in the list + if name != self.name: + self.contacts[name] = (name, domain, interface, protocol, host, address, port, + bare_name, txt) + self.new_serviceCB(name) + else: + # remember data + # In case this is not our own record but of another + # gajim instance on the same machine, + # it will be used when we get a new name. + self.invalid_self_contact[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt) + + + # different handler when resolving all contacts + def service_resolved_all_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): + if not self.connected: + return + bare_name = name + if name.find('@') == -1: + name = name + '@' + name + self.contacts[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt) + + def service_added_callback(self): + gajim.log.debug('Service successfully added') + + def service_committed_callback(self): + gajim.log.debug('Service successfully committed') + + def service_updated_callback(self): + gajim.log.debug('Service successfully updated') + + def service_add_fail_callback(self, err): + gajim.log.debug('Error while adding service. %s' % str(err)) + if str(err) == 'Local name collision': + alternative_name = self.server.GetAlternativeServiceName(self.username) + self.name_conflictCB(alternative_name) + return + self.error_CB(_('Error while adding service. %s') % str(err)) + self.disconnect() + + def server_state_changed_callback(self, state, error): + if state == self.avahi.SERVER_RUNNING: + self.create_service() + elif state == self.avahi.SERVER_COLLISION: + self.entrygroup.Reset() + elif state == self.avahi.CLIENT_FAILURE: + # does it ever go here? + gajim.log.debug('CLIENT FAILURE') + + def entrygroup_state_changed_callback(self, state, error): + # the name is already present, so recreate + if state == self.avahi.ENTRY_GROUP_COLLISION: + self.service_add_fail_callback('Local name collision') + elif state == self.avahi.ENTRY_GROUP_FAILURE: + gajim.log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached(that' + ' should not happen)') + + # make zeroconf-valid names + def replace_show(self, show): + if show in ['chat', 'online', '']: + return 'avail' + elif show == 'xa': + return 'away' + return show + + def avahi_txt(self): + utf8_dict = {} + for key in self.txt: + val = self.txt[key] + if isinstance(val, unicode): + utf8_dict[key] = val.encode('utf-8') + else: + utf8_dict[key] = val + return self.avahi.dict_to_txt_array(utf8_dict) + + def create_service(self): + try: + if not self.entrygroup: + # create an EntryGroup for publishing + self.entrygroup = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, self.server.EntryGroupNew()), self.avahi.DBUS_INTERFACE_ENTRY_GROUP) + self.entrygroup.connect_to_signal('StateChanged', self.entrygroup_state_changed_callback) + + txt = {} + + #remove empty keys + for key,val in self.txt.iteritems(): + if val: + txt[key] = val + + txt['port.p2pj'] = self.port + txt['version'] = 1 + txt['txtvers'] = 1 + + # replace gajim's show messages with compatible ones + if self.txt.has_key('status'): + txt['status'] = self.replace_show(self.txt['status']) + else: + txt['status'] = 'avail' + + self.txt = txt + gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype)) + self.entrygroup.AddService(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '', '', self.port, self.avahi_txt(), reply_handler=self.service_added_callback, error_handler=self.service_add_fail_callback) + + self.entrygroup.Commit(reply_handler=self.service_committed_callback, + error_handler=self.entrygroup_commit_error_CB) + + return True + + except dbus.DBusException, e: + gajim.log.debug(str(e)) + return False + + def announce(self): + if not self.connected: + return False + + state = self.server.GetState() + if state == self.avahi.SERVER_RUNNING: + self.create_service() + self.announced = True + return True + + def remove_announce(self): + if self.announced == False: + return False + try: + if self.entrygroup.GetState() != self.avahi.ENTRY_GROUP_FAILURE: + self.entrygroup.Reset() + self.entrygroup.Free() + # .Free() has mem leaks + obj = self.entrygroup._obj + obj._bus = None + self.entrygroup._obj = None + self.entrygroup = None + self.announced = False + + return True + else: + return False + except dbus.DBusException, e: + gajim.log.debug("Can't remove service. That should not happen") + + def browse_domain(self, interface, protocol, domain): + self.new_service_type(interface, protocol, self.stype, domain, '') + + def avahi_dbus_connect_cb(self, a, connect, disconnect): + if connect != "": + gajim.log.debug('Lost connection to avahi-daemon') + self.disconnect() + if self.disconnected_CB: + self.disconnected_CB() + else: + gajim.log.debug('We are connected to avahi-daemon') + + # connect to dbus + def connect_dbus(self): + try: + import dbus + except ImportError: + gajim.log.debug('Error: python-dbus needs to be installed. No zeroconf support.') + return False + if self.bus: + return True + try: + self.bus = dbus.SystemBus() + self.bus.add_signal_receiver(self.avahi_dbus_connect_cb, + "NameOwnerChanged", "org.freedesktop.DBus", + arg0="org.freedesktop.Avahi") + except Exception, e: + # System bus is not present + self.bus = None + gajim.log.debug(str(e)) + return False + else: + return True + + # connect to avahi + def connect_avahi(self): + if not self.connect_dbus(): + return False + try: + import avahi + self.avahi = avahi + except ImportError: + gajim.log.debug('Error: python-avahi needs to be installed. No zeroconf support.') + return False + + if self.server: + return True + try: + self.server = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ + self.avahi.DBUS_PATH_SERVER), self.avahi.DBUS_INTERFACE_SERVER) + self.server.connect_to_signal('StateChanged', + self.server_state_changed_callback) + except Exception, e: + # Avahi service is not present + self.server = None + gajim.log.debug(str(e)) + return False + else: + return True + + def connect(self): + self.name = self.username + '@' + self.host # service name + if not self.connect_avahi(): + return False + + self.connected = True + # start browsing + if self.domain is None: + # Explicitly browse .local + self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, "local") + + # Browse for other browsable domains + self.domain_browser = dbus.Interface(self.bus.get_object(self.avahi.DBUS_NAME, \ + self.server.DomainBrowserNew(self.avahi.IF_UNSPEC, \ + self.avahi.PROTO_UNSPEC, '', self.avahi.DOMAIN_BROWSER_BROWSE,\ + dbus.UInt32(0))), self.avahi.DBUS_INTERFACE_DOMAIN_BROWSER) + self.domain_browser.connect_to_signal('ItemNew', self.new_domain_callback) + self.domain_browser.connect_to_signal('Failure', self.error_callback) + else: + self.browse_domain(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, self.domain) + + return True + + def disconnect(self): + if self.connected: + self.connected = False + if self.service_browser: + self.service_browser.Free() + self.service_browser._obj._bus = None + self.service_browser._obj = None + if self.domain_browser: + self.domain_browser.Free() + self.domain_browser._obj._bus = None + self.domain_browser._obj = None + self.remove_announce() + self.server._obj._bus = None + self.server._obj = None + self.server = None + self.service_browser = None + self.domain_browser = None + + # refresh txt data of all contacts manually (no callback available) + def resolve_all(self): + if not self.connected: + return + for val in self.contacts.values(): + self.server.ResolveService(int(val[C_INTERFACE]), int(val[C_PROTOCOL]), + val[C_BARE_NAME], self.stype, val[C_DOMAIN], + self.avahi.PROTO_UNSPEC, dbus.UInt32(0), + reply_handler=self.service_resolved_all_callback, + error_handler=self.error_callback) + + def get_contacts(self): + return self.contacts + + def get_contact(self, jid): + if not jid in self.contacts: + return None + return self.contacts[jid] + + def update_txt(self, show = None): + if show: + self.txt['status'] = self.replace_show(show) + + txt = self.avahi_txt() + if self.connected and self.entrygroup: + self.entrygroup.UpdateServiceTxt(self.avahi.IF_UNSPEC, self.avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype,'', txt, reply_handler=self.service_updated_callback, error_handler=self.error_callback) + return True + else: + return False + + +# END Zeroconf diff --git a/src/common/zeroconf/zeroconf_bonjour.py b/src/common/zeroconf/zeroconf_bonjour.py new file mode 100644 index 000000000..3b7207004 --- /dev/null +++ b/src/common/zeroconf/zeroconf_bonjour.py @@ -0,0 +1,329 @@ +## common/zeroconf/zeroconf_bonjour.py +## +## Copyright (C) 2006 Stefan Bethge +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 2 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from common import gajim +import sys +import select +from string import split +from common.zeroconf.zeroconf import C_BARE_NAME, C_DOMAIN + +try: + import pybonjour +except ImportError, e: + pass + + +resolve_timeout = 1 + +class Zeroconf: + def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB, + disconnected_CB, error_CB, name, host, port): + self.domain = None # specific domain to browse + self.stype = '_presence._tcp' + self.port = port # listening port that gets announced + self.username = name + self.host = host + self.txt = pybonjour.TXTRecord() # service data + + # XXX these CBs should be set to None when we destroy the object + # (go offline), because they create a circular reference + self.new_serviceCB = new_serviceCB + self.remove_serviceCB = remove_serviceCB + self.name_conflictCB = name_conflictCB + self.disconnected_CB = disconnected_CB + self.error_CB = error_CB + + self.contacts = {} # all current local contacts with data + self.connected = False + self.announced = False + self.invalid_self_contact = {} + self.resolved = [] + + + def browse_callback(self, sdRef, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain): + gajim.log.debug('Found service %s in domain %s on %i(type: %s).' % (serviceName, replyDomain, interfaceIndex, regtype)) + if not self.connected: + return + if errorCode != pybonjour.kDNSServiceErr_NoError: + return + if not (flags & pybonjour.kDNSServiceFlagsAdd): + self.remove_service_callback(serviceName) + return + + # asynchronous resolving + resolve_sdRef = pybonjour.DNSServiceResolve(0, interfaceIndex, serviceName, regtype, replyDomain, self.service_resolved_callback) + + try: + while not self.resolved: + ready = select.select([resolve_sdRef], [], [], resolve_timeout) + if resolve_sdRef not in ready[0]: + gajim.log.debug('Resolve timed out') + break + pybonjour.DNSServiceProcessResult(resolve_sdRef) + else: + self.resolved.pop() + finally: + resolve_sdRef.close() + + def remove_service_callback(self, name): + gajim.log.debug('Service %s disappeared.' % name) + if not self.connected: + return + if name != self.name: + for key in self.contacts.keys(): + if self.contacts[key][C_BARE_NAME] == name: + del self.contacts[key] + self.remove_serviceCB(key) + return + + def new_domain_callback(self,interface, protocol, domain, flags): + if domain != "local": + self.browse_domain(interface, protocol, domain) + + # takes a TXTRecord instance + def txt_array_to_dict(self, txt): + items = pybonjour.TXTRecord.parse(txt)._items + dict = {} + for val in items.values(): + dict[val[0]] = val[1] + return dict + + def service_resolved_callback(self, sdRef, flags, interfaceIndex, errorCode, fullname, + hosttarget, port, txtRecord): + + # TODO: do proper decoding... + escaping= { + r'\.': '.', + r'\032': ' ', + r'\064': '@', + } + + name, stype, protocol, domain, dummy = split(fullname, '.') + + # Replace the escaped values + for src, trg in escaping.items(): + name = name.replace(src, trg) + + txt = pybonjour.TXTRecord.parse(txtRecord) + + gajim.log.debug('Service data for service %s on %i:' % (fullname, interfaceIndex)) + gajim.log.debug('Host %s, port %i, TXT data: %s' % (hosttarget, port, txt._items)) + + if not self.connected: + return + + bare_name = name + if '@' not in name: + name = name + '@' + name + + # we don't want to see ourselves in the list + if name != self.name: + self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord) + + self.new_serviceCB(name) + else: + # remember data + # In case this is not our own record but of another + # gajim instance on the same machine, + # it will be used when we get a new name. + self.invalid_self_contact[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord) + # count services + self.resolved.append(True) + + # different handler when resolving all contacts + def service_resolved_all_callback(self, sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord): + if not self.connected: + return + + escaping= { + r'\.': '.', + r'\032': ' ', + r'\064': '@', + } + + name, stype, protocol, domain, dummy = split(fullname, '.') + + # Replace the escaped values + for src, trg in escaping.items(): + name = name.replace(src, trg) + + bare_name = name + if name.find('@') == -1: + name = name + '@' + name + + # we don't want to see ourselves in the list + if name != self.name: + self.contacts[name] = (name, domain, interfaceIndex, protocol, hosttarget, hosttarget, port, bare_name, txtRecord) + + + def service_added_callback(self, sdRef, flags, errorCode, name, regtype, domain): + if errorCode == pybonjour.kDNSServiceErr_NoError: + gajim.log.debug('Service successfully added') + + def service_add_fail_callback(self, err): + if err[0][0] == pybonjour.kDNSServiceErr_NameConflict: + gajim.log.debug('Error while adding service. %s' % str(err)) + parts = self.username.split(' ') + + #check if last part is a number and if, increment it + try: + stripped = str(int(parts[-1])) + except: + stripped = 1 + alternative_name = self.username + str(stripped+1) + self.name_conflictCB(alternative_name) + return + self.error_CB(_('Error while adding service. %s') % str(err)) + self.disconnect() + + # make zeroconf-valid names + def replace_show(self, show): + if show in ['chat', 'online', '']: + return 'avail' + elif show == 'xa': + return 'away' + return show + + def create_service(self): + txt = {} + + #remove empty keys + for key,val in self.txt: + if val: + txt[key] = val + + txt['port.p2pj'] = self.port + txt['version'] = 1 + txt['txtvers'] = 1 + + # replace gajim's show messages with compatible ones + if 'status' in self.txt: + txt['status'] = self.replace_show(self.txt['status']) + else: + txt['status'] = 'avail' + + self.txt = pybonjour.TXTRecord(txt, strict=True) + + try: + sdRef = pybonjour.DNSServiceRegister(name = self.name, + regtype = self.stype, port = self.port, txtRecord = self.txt, + callBack = self.service_added_callback) + self.service_sdRef = sdRef + except pybonjour.BonjourError, e: + self.service_add_fail_callback(e) + else: + gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype)) + + ready = select.select([sdRef], [], [], resolve_timeout) + if sdRef in ready[0]: + pybonjour.DNSServiceProcessResult(sdRef) + + def announce(self): + if not self.connected: + return False + + self.create_service() + self.announced = True + return True + + def remove_announce(self): + if self.announced == False: + return False + try: + self.service_sdRef.close() + self.announced = False + return True + except pybonjour.BonjourError, e: + geajim.log.debug(e) + return False + + + def connect(self): + self.name = self.username + '@' + self.host # service name + + self.connected = True + + # start browsing + if self.domain is None: + # Explicitly browse .local + self.browse_domain() + + # Browse for other browsable domains + #self.domain_sdRef = pybonjour.DNSServiceEnumerateDomains(flags, interfaceIndex=0, callBack=self.new_domain_callback) + + else: + self.browse_domain(self.domain) + + return True + + def disconnect(self): + if self.connected: + self.connected = False + self.browse_sdRef.close() + self.remove_announce() + + + def browse_domain(self, domain=None): + gajim.log.debug('starting to browse') + try: + self.browse_sdRef = pybonjour.DNSServiceBrowse(regtype=self.stype, domain=domain, callBack=self.browse_callback) + except pybonjour.BonjourError, e: + self.error_CB("Error while browsing: %s" % e) + + def browse_loop(self): + ready = select.select([self.browse_sdRef], [], [], 2) + if self.browse_sdRef in ready[0]: + pybonjour.DNSServiceProcessResult(self.browse_sdRef) + + # refresh txt data of all contacts manually (no callback available) + def resolve_all(self): + if not self.connected: + return + + # for now put here as this is synchronous + self.browse_loop() + + for val in self.contacts.values(): + resolve_sdRef = pybonjour.DNSServiceResolve(0, + pybonjour.kDNSServiceInterfaceIndexAny, val[C_BARE_NAME], + self.stype + '.', val[C_DOMAIN] + '.', + self.service_resolved_all_callback) + + try: + ready = select.select([resolve_sdRef], [], [], resolve_timeout) + if resolve_sdRef not in ready[0]: + gajim.log.debug('Resolve timed out (in resolve_all)') + break + pybonjour.DNSServiceProcessResult(resolve_sdRef) + finally: + resolve_sdRef.close() + + def get_contacts(self): + return self.contacts + + def get_contact(self, jid): + if not jid in self.contacts: + return None + return self.contacts[jid] + + def update_txt(self, show = None): + if show: + self.txt['status'] = self.replace_show(show) + + try: + pybonjour.DNSServiceUpdateRecord(self.service_sdRef, None, 0, self.txt) + except pybonjour.BonjourError, e: + return False + return True + diff --git a/src/config.py b/src/config.py index f3f329c41..69d4b3b00 100644 --- a/src/config.py +++ b/src/config.py @@ -89,10 +89,7 @@ class PreferencesWindow: self.treat_incoming_messages_combobox =\ self.xml.get_widget('treat_incoming_messages_combobox') - #FIXME: remove when ANC will be implemented - w = self.xml.get_widget('hbox3020') - w.set_no_show_all(True) - w.hide() + w = self.xml.get_widget('anc_hbox') # trayicon if gajim.interface.systray_capabilities: @@ -146,6 +143,8 @@ class PreferencesWindow: # iconset iconsets_list = os.listdir(os.path.join(gajim.DATA_DIR, 'iconsets')) + if os.path.isdir(gajim.MY_ICONSETS_PATH): + iconsets_list += os.listdir(gajim.MY_ICONSETS_PATH) # new model, image in 0, string in 1 model = gtk.ListStore(gtk.Image, str) renderer_image = cell_renderer_image.CellRendererImage(0, 0) @@ -158,7 +157,8 @@ class PreferencesWindow: self.iconset_combobox.set_model(model) l = [] for dir in iconsets_list: - if not os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', dir)): + if not os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', dir)) \ + and not os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, dir)): continue if dir != '.svn' and dir != 'transports': l.append(dir) @@ -167,9 +167,9 @@ class PreferencesWindow: for i in xrange(len(l)): preview = gtk.Image() files = [] - files.append(os.path.join(gajim.DATA_DIR, 'iconsets', l[i], '16x16', + files.append(os.path.join(helpers.get_iconset_path(l[i]), '16x16', 'online.png')) - files.append(os.path.join(gajim.DATA_DIR, 'iconsets', l[i], '16x16', + files.append(os.path.join(helpers.get_iconset_path(l[i]), '16x16', 'online.gif')) for file in files: if os.path.exists(file): @@ -186,6 +186,10 @@ class PreferencesWindow: else: self.one_window_type_combobox.set_active(0) + # Compact View + st = gajim.config.get('compact_view') + self.xml.get_widget('compact_view_checkbutton').set_active(st) + # Set default for treat incoming messages choices = common.config.opt_treat_incoming_messages type = gajim.config.get('treat_incoming_messages') @@ -203,16 +207,7 @@ class PreferencesWindow: cell = gtk.CellRendererText() theme_combobox.pack_start(cell, True) theme_combobox.add_attribute(cell, 'text', 0) - model = gtk.ListStore(str) - theme_combobox.set_model(model) - - i = 0 - for config_theme in gajim.config.get_per('themes'): - theme = config_theme.replace('_', ' ') - model.append([theme]) - if gajim.config.get('roster_theme') == config_theme: - theme_combobox.set_active(i) - i += 1 + self.update_theme_list() # use speller if os.name == 'nt': @@ -394,6 +389,10 @@ class PreferencesWindow: self.auto_xa_message_entry.set_text(st) self.auto_xa_message_entry.set_sensitive(gajim.config.get('autoxa')) + from common import sleepy + if not sleepy.SUPPORTED: + self.xml.get_widget('autoaway_table').set_sensitive(False) + # ask_status when online / offline st = gajim.config.get('ask_online_status') self.xml.get_widget('prompt_online_status_message_checkbutton').\ @@ -504,8 +503,7 @@ class PreferencesWindow: notify_gmail_checkbutton = self.xml.get_widget('notify_gmail_checkbutton') notify_gmail_extra_checkbutton = self.xml.get_widget( 'notify_gmail_extra_checkbutton') - frame_gmail.set_no_show_all(True) - + for account in gajim.config.get_per('accounts'): jid = gajim.get_jid_from_account(account) if gajim.get_server_from_jid(jid) in gajim.gmail_domains: @@ -580,7 +578,14 @@ class PreferencesWindow: def on_show_avatars_in_roster_checkbutton_toggled(self, widget): self.on_checkbutton_toggled(widget, 'show_avatars_in_roster') gajim.interface.roster.draw_roster() - + # Redraw connected groupchats (in an ugly way) + for account in gajim.connections: + if gajim.connections[account].connected: + for gc_control in gajim.interface.msg_win_mgr.get_controls( + message_control.TYPE_GC) + \ + gajim.interface.minimized_controls[account].values(): + gc_control.draw_roster() + def on_emoticons_combobox_changed(self, widget): active = widget.get_active() model = widget.get_model() @@ -630,6 +635,18 @@ class PreferencesWindow: gajim.interface.roster.change_roster_style(None) gajim.interface.save_config() + def update_theme_list(self): + theme_combobox = self.xml.get_widget('theme_combobox') + model = gtk.ListStore(str) + theme_combobox.set_model(model) + i = 0 + for config_theme in gajim.config.get_per('themes'): + theme = config_theme.replace('_', ' ') + model.append([theme]) + if gajim.config.get('roster_theme') == config_theme: + theme_combobox.set_active(i) + i += 1 + def on_open_advanced_notifications_button_clicked(self, widget): dialogs.AdvancedNotificationsWindow() @@ -668,6 +685,15 @@ class PreferencesWindow: if spell_obj: spell_obj.detach() + def on_compact_view_checkbutton_toggled(self, widget): + active = widget.get_active() + for ctl in gajim.interface.msg_win_mgr.controls(): + ctl.chat_buttons_set_visible(active) + gajim.config.set('compact_view', active) + gajim.interface.save_config() + gajim.interface.roster.draw_roster() + + def on_speller_checkbutton_toggled(self, widget): active = widget.get_active() gajim.config.set('use_speller', active) @@ -1129,539 +1155,6 @@ class PreferencesWindow: gajim.interface.roster.enable_syncing_status_msg_from_current_music_track( widget.get_active()) -#---------- AccountModificationWindow class -------------# -class AccountModificationWindow: - '''Class for account informations''' - def on_account_modification_window_destroy(self, widget): - '''close window''' - if gajim.interface.instances.has_key(self.account): - if gajim.interface.instances[self.account].has_key( - 'account_modification'): - del gajim.interface.instances[self.account]['account_modification'] - return - if gajim.interface.instances.has_key('account_modification'): - del gajim.interface.instances['account_modification'] - - def on_cancel_button_clicked(self, widget): - self.window.destroy() - - def __init__(self, account): - self.xml = gtkgui_helpers.get_glade('account_modification_window.glade') - self.window = self.xml.get_widget('account_modification_window') - self.window.set_transient_for(gajim.interface.roster.window) - self.account = account - - # init proxy list - self.update_proxy_list() - - self.xml.signal_autoconnect(self) - self.init_account() - self.xml.get_widget('save_button').grab_focus() - self.window.show_all() - - def on_checkbutton_toggled(self, widget, widgets): - '''set or unset sensitivity of widgets when widget is toggled''' - for w in widgets: - w.set_sensitive(widget.get_active()) - - def init_account_gpg(self): - keyid = gajim.config.get_per('accounts', self.account, 'keyid') - keyname = gajim.config.get_per('accounts', self.account, 'keyname') - savegpgpass = gajim.config.get_per('accounts', self.account, - 'savegpgpass') - - if not keyid or not gajim.config.get('usegpg'): - return - - self.xml.get_widget('gpg_key_label').set_text(keyid) - self.xml.get_widget('gpg_name_label').set_text(keyname) - gpg_save_password_checkbutton = \ - self.xml.get_widget('gpg_save_password_checkbutton') - gpg_save_password_checkbutton.set_sensitive(True) - gpg_save_password_checkbutton.set_active(savegpgpass) - - if savegpgpass: - entry = self.xml.get_widget('gpg_password_entry') - entry.set_sensitive(True) - gpgpassword = gajim.config.get_per('accounts', - self.account, 'gpgpassword') - entry.set_text(gpgpassword) - - def update_proxy_list(self): - if self.account: - our_proxy = gajim.config.get_per('accounts', self.account, 'proxy') - else: - our_proxy = '' - if not our_proxy: - our_proxy = _('None') - self.proxy_combobox = self.xml.get_widget('proxies_combobox') - model = gtk.ListStore(str) - self.proxy_combobox.set_model(model) - l = gajim.config.get_per('proxies') - l.insert(0, _('None')) - for i in xrange(len(l)): - model.append([l[i]]) - if our_proxy == l[i]: - self.proxy_combobox.set_active(i) - - def init_account(self): - '''Initialize window with defaults values''' - self.xml.get_widget('name_entry').set_text(self.account) - jid = gajim.config.get_per('accounts', self.account, 'name') \ - + '@' + gajim.config.get_per('accounts', - self.account, 'hostname') - self.xml.get_widget('jid_entry').set_text(jid) - self.xml.get_widget('save_password_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, 'savepass')) - if gajim.config.get_per('accounts', self.account, 'savepass'): - passstr = passwords.get_password(self.account) or '' - password_entry = self.xml.get_widget('password_entry') - password_entry.set_sensitive(True) - password_entry.set_text(passstr) - - self.xml.get_widget('resource_entry').set_text(gajim.config.get_per( - 'accounts', self.account, 'resource')) - self.xml.get_widget('adjust_priority_with_status_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, - 'adjust_priority_with_status')) - spinbutton = self.xml.get_widget('priority_spinbutton') - if gajim.config.get('enable_negative_priority'): - spinbutton.set_range(-128, 127) - spinbutton.set_value(gajim.config.get_per('accounts', self.account, - 'priority')) - - usessl = gajim.config.get_per('accounts', self.account, 'usessl') - self.xml.get_widget('use_ssl_checkbutton').set_active(usessl) - - self.xml.get_widget('send_keepalive_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, - 'keep_alives_enabled')) - - use_custom_host = gajim.config.get_per('accounts', self.account, - 'use_custom_host') - self.xml.get_widget('custom_host_port_checkbutton').set_active( - use_custom_host) - custom_host = gajim.config.get_per('accounts', self.account, - 'custom_host') - if not custom_host: - custom_host = gajim.config.get_per('accounts', - self.account, 'hostname') - self.xml.get_widget('custom_host_entry').set_text(custom_host) - custom_port = gajim.config.get_per('accounts', self.account, - 'custom_port') - if not custom_port: - custom_port = 5222 - self.xml.get_widget('custom_port_entry').set_text(unicode(custom_port)) - - gpg_key_label = self.xml.get_widget('gpg_key_label') - if gajim.config.get('usegpg'): - self.init_account_gpg() - else: - gpg_key_label.set_text(_('OpenPGP is not usable in this computer')) - self.xml.get_widget('gpg_choose_button').set_sensitive(False) - self.xml.get_widget('autoconnect_checkbutton').set_active(gajim.config.\ - get_per('accounts', self.account, 'autoconnect')) - self.xml.get_widget('autoreconnect_checkbutton').set_active(gajim.config.\ - get_per('accounts', self.account, 'autoreconnect')) - - self.xml.get_widget('sync_with_global_status_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, - 'sync_with_global_status')) - self.xml.get_widget('autoconnect_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, 'autoconnect')) - self.xml.get_widget('use_ft_proxies_checkbutton').set_active( - gajim.config.get_per('accounts', self.account, 'use_ft_proxies')) - list_no_log_for = gajim.config.get_per('accounts', self.account, - 'no_log_for').split() - if self.account in list_no_log_for: - self.xml.get_widget('log_history_checkbutton').set_active(0) - - def option_changed(self, config, opt): - if gajim.config.get_per('accounts', self.account, opt) != config[opt]: - return True - return False - - def options_changed_need_relogin(self, config, options): - '''accepts configuration and options - (tupple of strings of the name of options changed) - and returns True or False depending on if at least one of the options - need relogin to server to apply''' - for option in options: - if self.option_changed(config, option): - return True - return False - - def on_adjust_priority_with_status_checkbutton_toggled(self, widget): - self.xml.get_widget('priority_spinbutton').set_sensitive( - not widget.get_active()) - - def on_save_button_clicked(self, widget): - '''When save button is clicked: Save information in config file''' - config = {} - name = self.xml.get_widget('name_entry').get_text().decode('utf-8') - if gajim.connections.has_key(self.account): - if name != self.account: - if gajim.connections[self.account].connected != 0: - dialogs.ErrorDialog( - _('You are currently connected to the server'), - _('To change the account name, you must be disconnected.')) - return - if len(gajim.events.get_events(self.account)): - dialogs.ErrorDialog(_('Unread events'), - _('To change the account name, you must read all pending ' - 'events.')) - return - if name in gajim.connections: - dialogs.ErrorDialog(_('Account Name Already Used'), - _('This name is already used by another of your accounts. ' - 'Please choose another name.')) - return - if (name == ''): - dialogs.ErrorDialog(_('Invalid account name'), - _('Account name cannot be empty.')) - return - if name.find(' ') != -1: - dialogs.ErrorDialog(_('Invalid account name'), - _('Account name cannot contain spaces.')) - return - jid = self.xml.get_widget('jid_entry').get_text().decode('utf-8') - - # check if jid is conform to RFC and stringprep it - try: - jid = helpers.parse_jid(jid) - except helpers.InvalidFormat, s: - pritext = _('Invalid Jabber ID') - dialogs.ErrorDialog(pritext, str(s)) - return - - n, hn = jid.split('@', 1) - if not n: - pritext = _('Invalid Jabber ID') - sectext = _('A Jabber ID must be in the form "user@servername".') - dialogs.ErrorDialog(pritext, sectext) - return - - resource = self.xml.get_widget('resource_entry').get_text().decode( - 'utf-8') - try: - resource = helpers.parse_resource(resource) - except helpers.InvalidFormat, s: - pritext = _('Invalid Jabber ID') - dialogs.ErrorDialog(pritext, (s)) - return - - config['savepass'] = self.xml.get_widget( - 'save_password_checkbutton').get_active() - config['password'] = self.xml.get_widget('password_entry').get_text().\ - decode('utf-8') - config['resource'] = resource - config['adjust_priority_with_status'] = self.xml.get_widget( - 'adjust_priority_with_status_checkbutton').get_active() - config['priority'] = self.xml.get_widget('priority_spinbutton').\ - get_value_as_int() - config['autoconnect'] = self.xml.get_widget('autoconnect_checkbutton').\ - get_active() - config['autoreconnect'] = self.xml.get_widget( - 'autoreconnect_checkbutton').get_active() - - if self.account: - list_no_log_for = gajim.config.get_per('accounts', self.account, - 'no_log_for').split() - else: - list_no_log_for = [] - if self.account in list_no_log_for: - list_no_log_for.remove(self.account) - if not self.xml.get_widget('log_history_checkbutton').get_active(): - list_no_log_for.append(name) - config['no_log_for'] = ' '.join(list_no_log_for) - - config['sync_with_global_status'] = self.xml.get_widget( - 'sync_with_global_status_checkbutton').get_active() - config['use_ft_proxies'] = self.xml.get_widget( - 'use_ft_proxies_checkbutton').get_active() - - active = self.proxy_combobox.get_active() - proxy = self.proxy_combobox.get_model()[active][0].decode('utf-8') - if proxy == _('None'): - proxy = '' - config['proxy'] = proxy - - config['usessl'] = self.xml.get_widget('use_ssl_checkbutton').get_active() - config['name'] = n - config['hostname'] = hn - - config['use_custom_host'] = self.xml.get_widget( - 'custom_host_port_checkbutton').get_active() - custom_port = self.xml.get_widget('custom_port_entry').get_text() - try: - custom_port = int(custom_port) - except: - dialogs.ErrorDialog(_('Invalid entry'), - _('Custom port must be a port number.')) - return - config['custom_port'] = custom_port - config['custom_host'] = self.xml.get_widget( - 'custom_host_entry').get_text().decode('utf-8') - - # update in case the name was changed to local account's name - config['is_zeroconf'] = False - - config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().\ - decode('utf-8') - if config['keyname'] == '': # no key selected - config['keyid'] = '' - config['savegpgpass'] = False - config['gpgpassword'] = '' - else: - config['keyid'] = self.xml.get_widget('gpg_key_label').get_text().\ - decode('utf-8') - config['savegpgpass'] = self.xml.get_widget( - 'gpg_save_password_checkbutton').get_active() - config['gpgpassword'] = self.xml.get_widget('gpg_password_entry' - ).get_text().decode('utf-8') - # if we modify the name of the account - if name != self.account: - # update variables - gajim.interface.instances[name] = gajim.interface.instances[ - self.account] - gajim.nicks[name] = gajim.nicks[self.account] - gajim.block_signed_in_notifications[name] = \ - gajim.block_signed_in_notifications[self.account] - gajim.groups[name] = gajim.groups[self.account] - gajim.gc_connected[name] = gajim.gc_connected[self.account] - gajim.automatic_rooms[name] = gajim.automatic_rooms[self.account] - gajim.newly_added[name] = gajim.newly_added[self.account] - gajim.to_be_removed[name] = gajim.to_be_removed[self.account] - gajim.sleeper_state[name] = gajim.sleeper_state[self.account] - gajim.encrypted_chats[name] = gajim.encrypted_chats[self.account] - gajim.last_message_time[name] = \ - gajim.last_message_time[self.account] - gajim.status_before_autoaway[name] = \ - gajim.status_before_autoaway[self.account] - gajim.transport_avatar[name] = gajim.transport_avatar[self.account] - - gajim.contacts.change_account_name(self.account, name) - gajim.events.change_account_name(self.account, name) - - # change account variable for chat / gc controls - gajim.interface.msg_win_mgr.change_account_name(self.account, name) - # upgrade account variable in opened windows - for kind in ('infos', 'disco', 'gc_config', 'search'): - for j in gajim.interface.instances[name][kind]: - gajim.interface.instances[name][kind][j].account = name - - # ServiceCache object keep old property account - if hasattr(gajim.connections[self.account], 'services_cache'): - gajim.connections[self.account].services_cache.account = name - del gajim.interface.instances[self.account] - del gajim.nicks[self.account] - del gajim.block_signed_in_notifications[self.account] - del gajim.groups[self.account] - del gajim.gc_connected[self.account] - del gajim.automatic_rooms[self.account] - del gajim.newly_added[self.account] - del gajim.to_be_removed[self.account] - del gajim.sleeper_state[self.account] - del gajim.encrypted_chats[self.account] - del gajim.last_message_time[self.account] - del gajim.status_before_autoaway[self.account] - del gajim.transport_avatar[self.account] - gajim.connections[self.account].name = name - gajim.connections[name] = gajim.connections[self.account] - del gajim.connections[self.account] - gajim.config.del_per('accounts', self.account) - gajim.config.add_per('accounts', name) - self.account = name - - resend_presence = False - if gajim.connections[self.account].connected == 0: # we're disconnected - relogin_needed = False - else: # we're connected to the account we want to apply changes - # check if relogin is needed - relogin_needed = False - if self.options_changed_need_relogin(config, - ('resource', 'proxy', 'usessl', 'keyname', - 'use_custom_host', 'custom_host')): - relogin_needed = True - - elif config['use_custom_host'] and (self.option_changed(config, - 'custom_host') or self.option_changed(config, 'custom_port')): - relogin_needed = True - - if self.option_changed(config, 'use_ft_proxies') and \ - config['use_ft_proxies']: - gajim.connections[self.account].discover_ft_proxies() - - if self.option_changed(config, 'priority') or self.option_changed( - config, 'adjust_priority_with_status'): - resend_presence = True - - for opt in config: - gajim.config.set_per('accounts', name, opt, config[opt]) - if config['savepass']: - passwords.save_password(name, config['password']) - else: - passwords.save_password(name, '') - # refresh accounts window - if gajim.interface.instances.has_key('accounts'): - gajim.interface.instances['accounts'].init_accounts() - # refresh roster - gajim.interface.roster.draw_roster() - gajim.interface.save_config() - self.window.destroy() - - if relogin_needed: - def login(account, show_before, status_before): - ''' login with previous status''' - # first make sure connection is really closed, - # 0.5 may not be enough - gajim.connections[account].disconnect(True) - gajim.interface.roster.send_status(account, show_before, - status_before) - - def relog(widget): - self.dialog.destroy() - show_before = gajim.SHOW_LIST[gajim.connections[self.account].\ - connected] - status_before = gajim.connections[self.account].status - gajim.interface.roster.send_status(self.account, 'offline', - _('Be right back.')) - gobject.timeout_add(500, login, self.account, show_before, - status_before) - - def resend(widget): - self.resend_presence() - - on_no = None - if resend_presence: - on_no = resend - self.dialog = dialogs.YesNoDialog(_('Relogin now?'), - _('If you want all the changes to apply instantly, ' - 'you must relogin.'), on_response_yes = relog, - on_response_no = on_no) - elif resend_presence: - self.resend_presence() - - def resend_presence(self): - show = gajim.SHOW_LIST[gajim.connections[self.account].connected] - status = gajim.connections[self.account].status - gajim.connections[self.account].change_status(show, status) - - def on_change_password_button_clicked(self, widget): - try: - dialog = dialogs.ChangePasswordDialog(self.account) - except GajimGeneralException: - #if we showed ErrorDialog, there will not be dialog instance - return - - new_password = dialog.run() - if new_password != -1: - gajim.connections[self.account].change_password(new_password) - if self.xml.get_widget('save_password_checkbutton').get_active(): - self.xml.get_widget('password_entry').set_text(new_password) - - def on_edit_details_button_clicked(self, widget): - if not gajim.interface.instances.has_key(self.account): - dialogs.ErrorDialog(_('No such account available'), - _('You must create your account before editing your personal ' - 'information.')) - return - - # show error dialog if account is newly created (not in gajim.connections) - if not gajim.connections.has_key(self.account) or \ - gajim.connections[self.account].connected < 2: - dialogs.ErrorDialog(_('You are not connected to the server'), - _('Without a connection, you can not edit your personal information.')) - return - - if not gajim.connections[self.account].vcard_supported: - dialogs.ErrorDialog(_("Your server doesn't support Vcard"), - _("Your server can't save your personal information.")) - return - - gajim.interface.edit_own_details(self.account) - - def on_manage_proxies_button_clicked(self, widget): - if gajim.interface.instances.has_key('manage_proxies'): - gajim.interface.instances['manage_proxies'].window.present() - else: - gajim.interface.instances['manage_proxies'] = \ - ManageProxiesWindow() - - def on_synchronise_contacts_button_clicked(self, widget): - try: - dialog = dialogs.SynchroniseSelectAccountDialog(self.account) - except GajimGeneralException: - # If we showed ErrorDialog, there will not be dialog instance - return - - def on_gpg_choose_button_clicked(self, widget, data = None): - if gajim.connections.has_key(self.account): - secret_keys = gajim.connections[self.account].ask_gpg_secrete_keys() - - # self.account is None and/or gajim.connections is {} - else: - from common import GnuPG - if GnuPG.USE_GPG: - secret_keys = GnuPG.GnuPG().get_secret_keys() - else: - secret_keys = [] - if not secret_keys: - dialogs.ErrorDialog(_('Failed to get secret keys'), - _('There was a problem retrieving your OpenPGP secret keys.')) - return - secret_keys[_('None')] = _('None') - instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'), - _('Choose your OpenPGP key'), secret_keys) - keyID = instance.run() - if keyID is None: - return - checkbutton = self.xml.get_widget('gpg_save_password_checkbutton') - gpg_key_label = self.xml.get_widget('gpg_key_label') - gpg_name_label = self.xml.get_widget('gpg_name_label') - if keyID[0] == _('None'): - gpg_key_label.set_text(_('No key selected')) - gpg_name_label.set_text('') - checkbutton.set_sensitive(False) - self.xml.get_widget('gpg_password_entry').set_sensitive(False) - else: - gpg_key_label.set_text(keyID[0]) - gpg_name_label.set_text(keyID[1]) - checkbutton.set_sensitive(True) - checkbutton.set_active(False) - self.xml.get_widget('gpg_password_entry').set_text('') - - def on_checkbutton_toggled_and_clear(self, widget, widgets): - self.on_checkbutton_toggled(widget, widgets) - for w in widgets: - if not widget.get_active(): - w.set_text('') - - def on_use_ssl_checkbutton_toggled(self, widget): - isactive = widget.get_active() - if isactive: - self.xml.get_widget('custom_port_entry').set_text('5223') - else: - self.xml.get_widget('custom_port_entry').set_text('5222') - - def on_send_keepalive_checkbutton_toggled(self, widget): - isactive = widget.get_active() - gajim.config.set_per('accounts', self.account, - 'keep_alives_enabled', isactive) - - def on_custom_host_port_checkbutton_toggled(self, widget): - isactive = widget.get_active() - self.xml.get_widget('custom_host_port_hbox').set_sensitive(isactive) - - def on_gpg_save_password_checkbutton_toggled(self, widget): - self.on_checkbutton_toggled_and_clear(widget, [ - self.xml.get_widget('gpg_password_entry')]) - - def on_save_password_checkbutton_toggled(self, widget): - self.on_checkbutton_toggled_and_clear(widget, - [self.xml.get_widget('password_entry')]) - self.xml.get_widget('password_entry').grab_focus() - #---------- ManageProxiesWindow class -------------# class ManageProxiesWindow: def __init__(self): @@ -1699,12 +1192,9 @@ class ManageProxiesWindow: self.xml.get_widget('proxytype_combobox').set_active(0) def on_manage_proxies_window_destroy(self, widget): - for account in gajim.connections: - if gajim.interface.instances[account].has_key('account_modification'): - gajim.interface.instances[account]['account_modification'].\ - update_proxy_list() - if gajim.interface.instances.has_key('account_modification'): - gajim.interface.instances['account_modification'].update_proxy_list() + if gajim.interface.instances.has_key('accounts'): + gajim.interface.instances['accounts'].\ + update_proxy_list() del gajim.interface.instances['manage_proxies'] def on_add_proxy_button_clicked(self, widget): @@ -1835,69 +1325,360 @@ class AccountsWindow: def on_close_button_clicked(self, widget): self.window.destroy() + def on_accounts_window_destroy(self, widget): + if gajim.interface.instances.has_key('accounts'): + del gajim.interface.instances['accounts'] + def __init__(self): self.xml = gtkgui_helpers.get_glade('accounts_window.glade') self.window = self.xml.get_widget('accounts_window') self.window.set_transient_for(gajim.interface.roster.window) self.accounts_treeview = self.xml.get_widget('accounts_treeview') - self.modify_button = self.xml.get_widget('modify_button') self.remove_button = self.xml.get_widget('remove_button') - model = gtk.ListStore(str, str, bool) + self.rename_button = self.xml.get_widget('rename_button') + #FIXME: I don't understand why this import is necessary + import os + path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps', + 'kbd_input.png') + img = self.xml.get_widget('rename_image') + img.set_from_file(path_to_kbd_input_img) + self.notebook = self.xml.get_widget('notebook') + # Name + model = gtk.ListStore(str) self.accounts_treeview.set_model(model) - #columns + # column renderer = gtk.CellRendererText() self.accounts_treeview.insert_column_with_attributes(-1, _('Name'), renderer, text = 0) - renderer = gtk.CellRendererText() - self.accounts_treeview.insert_column_with_attributes(-1, - _('Server'), renderer, text = 1) + + self.current_account = None + # When we fill info, we don't want to handle the changed signals + self.ignore_events = False + self.need_relogin = False + self.resend_presence = False + + self.update_proxy_list() self.xml.signal_autoconnect(self) self.init_accounts() self.window.show_all() - #Merge accounts + # Merge accounts st = gajim.config.get('mergeaccounts') self.xml.get_widget('merge_checkbutton').set_active(st) import os - avahi_error = False + self.avahi_available = True try: import avahi except ImportError: - avahi_error = True - - # enable zeroconf - st = gajim.config.get('enable_zeroconf') - w = self.xml.get_widget('enable_zeroconf_checkbutton') - w.set_active(st) - if os.name == 'nt' or (avahi_error and not w.get_active()): - w.set_sensitive(False) - self.zeroconf_toggled_id = w.connect('toggled', - self.on_enable_zeroconf_checkbutton_toggled) + self.avahi_available = False def on_accounts_window_key_press_event(self, widget, event): if event.keyval == gtk.keysyms.Escape: self.window.destroy() + def select_account(self, account): + model = self.accounts_treeview.get_model() + iter = model.get_iter_root() + while iter: + acct = model[iter][0].decode('utf-8') + if account == acct: + self.accounts_treeview.set_cursor(model.get_path(iter)) + return + iter = model.iter_next(iter) + def init_accounts(self): '''initialize listStore with existing accounts''' - self.modify_button.set_sensitive(False) self.remove_button.set_sensitive(False) + self.rename_button.set_sensitive(False) + self.current_account = None + self.init_account() model = self.accounts_treeview.get_model() model.clear() - for account in gajim.connections: + for account in gajim.config.get_per('accounts'): iter = model.append() - model.set(iter, 0, account, 1, gajim.get_hostname_from_account( - account)) + model.set(iter, 0, account) + + def resend(self): + show = gajim.SHOW_LIST[gajim.connections[self.current_account].connected] + status = gajim.connections[self.current_account].status + gajim.connections[self.current_account].change_status(show, status) def on_accounts_treeview_cursor_changed(self, widget): - '''Activate delete and modify buttons when a row is selected''' - self.modify_button.set_sensitive(True) - self.remove_button.set_sensitive(True) + '''Activate modify buttons when a row is selected, update accounts info''' + sel = self.accounts_treeview.get_selection() + (model, iter) = sel.get_selected() + if iter: + account = model[iter][0].decode('utf-8') + else: + account = None + if self.current_account and self.current_account == account: + # We're comming back to our current account, no need to update widgets + return + # Save config for previous account if needed cause focus_out event is + # called after the changed event + if self.current_account: + focused_widget = self.window.get_focus() + focused_widget_name = focused_widget.get_name() + if focused_widget_name in ('jid_entry1', 'resource_entry1', + 'custom_port_entry'): + if focused_widget_name == 'jid_entry1': + func = self.on_jid_entry1_focus_out_event + elif focused_widget_name == 'resource_entry1': + func = self.on_resource_entry1_focus_out_event + elif focused_widget_name == 'custom_port_entry': + func = self.on_custom_port_entry_focus_out_event + if func(focused_widget, None): + # Error detected in entry, don't change account, re-put cursor on + # previous row + self.select_account(self.current_account) + return True + self.window.set_focus(widget) - def on_new_button_clicked(self, widget): - '''When new button is clicked: open an account information window''' + if self.need_relogin and self.current_account == gajim.ZEROCONF_ACC_NAME: + if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME): + gajim.connections[gajim.ZEROCONF_ACC_NAME].update_details() + return + + elif self.need_relogin and gajim.connections[self.current_account].\ + connected > 0: + def login(account, show_before, status_before): + ''' login with previous status''' + # first make sure connection is really closed, + # 0.5 may not be enough + gajim.connections[account].disconnect(True) + gajim.interface.roster.send_status(account, show_before, + status_before) + + def relog(): + self.dialog.destroy() + show_before = gajim.SHOW_LIST[gajim.connections[ + self.current_account].connected] + status_before = gajim.connections[self.current_account].status + gajim.interface.roster.send_status(self.current_account, 'offline', + _('Be right back.')) + gobject.timeout_add(500, login, self.current_account, show_before, + status_before) + + self.dialog = dialogs.YesNoDialog(_('Relogin now?'), + _('If you want all the changes to apply instantly, ' + 'you must relogin.')) + resp = self.dialog.get_response() + if resp == gtk.RESPONSE_YES: + relog() + elif self.resend_presence: + self.resend() + elif self.resend_presence: + self.resend() + + self.need_relogin = False + self.resend_presence = False + self.remove_button.set_sensitive(True) + self.rename_button.set_sensitive(True) + if iter: + self.current_account = account + if account == gajim.ZEROCONF_ACC_NAME: + self.remove_button.set_sensitive(False) + self.rename_button.set_sensitive(False) + self.init_account() + self.update_proxy_list() + + def update_proxy_list(self): + if self.current_account: + our_proxy = gajim.config.get_per('accounts', self.current_account, + 'proxy') + else: + our_proxy = '' + + if not our_proxy: + our_proxy = _('None') + proxy_combobox = self.xml.get_widget('proxies_combobox1') + model = gtk.ListStore(str) + proxy_combobox.set_model(model) + l = gajim.config.get_per('proxies') + l.insert(0, _('None')) + for i in xrange(len(l)): + model.append([l[i]]) + if our_proxy == l[i]: + proxy_combobox.set_active(i) + + def init_account(self): + if not self.current_account: + self.notebook.set_current_page(0) + return + if gajim.config.get_per('accounts', self.current_account, 'is_zeroconf'): + self.ignore_events = True + self.init_zeroconf_account() + self.ignore_events = False + self.notebook.set_current_page(2) + return + self.ignore_events = True + self.init_normal_account() + self.ignore_events = False + self.notebook.set_current_page(1) + + def init_zeroconf_account(self): + enable = gajim.config.get('enable_zeroconf') + self.xml.get_widget('enable_zeroconf_checkbutton2').set_active(enable) + self.xml.get_widget('zeroconf_notebook').set_sensitive(enable) + # General tab + st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'autoconnect') + self.xml.get_widget('autoconnect_checkbutton2').set_active(st) + + list_no_log_for = gajim.config.get_per('accounts', + gajim.ZEROCONF_ACC_NAME, 'no_log_for').split() + if gajim.ZEROCONF_ACC_NAME in list_no_log_for: + self.xml.get_widget('log_history_checkbutton2').set_active(0) + else: + self.xml.get_widget('log_history_checkbutton2').set_active(1) + + st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'sync_with_global_status') + self.xml.get_widget('sync_with_global_status_checkbutton2').set_active(st) + + st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'use_custom_host') + self.xml.get_widget('custom_port_checkbutton2').set_active(st) + self.xml.get_widget('custom_port_entry2').set_sensitive(st) + + st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'custom_port') + if not st: + gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'custom_port', '5298') + st = '5298' + self.xml.get_widget('custom_port_entry2').set_text(str(st)) + + # Personal tab + gpg_key_label = self.xml.get_widget('gpg_key_label2') + if gajim.config.get('usegpg'): + self.xml.get_widget('gpg_choose_button2').set_sensitive(True) + self.init_account_gpg() + else: + gpg_key_label.set_text(_('OpenPGP is not usable in this computer')) + self.xml.get_widget('gpg_choose_button2').set_sensitive(False) + + for opt in ('first_name', 'last_name', 'jabber_id', 'email'): + st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, + 'zeroconf_' + opt) + self.xml.get_widget(opt + '_entry2').set_text(st) + + def init_account_gpg(self): + account = self.current_account + keyid = gajim.config.get_per('accounts', account, 'keyid') + keyname = gajim.config.get_per('accounts', account, 'keyname') + savegpgpass = gajim.config.get_per('accounts', account, 'savegpgpass') + + if account == gajim.ZEROCONF_ACC_NAME: + widget_name_add = '2' + else: + widget_name_add = '1' + + gpg_key_label = self.xml.get_widget('gpg_key_label' + widget_name_add) + gpg_name_label = self.xml.get_widget('gpg_name_label' + widget_name_add) + gpg_save_password_checkbutton = \ + self.xml.get_widget('gpg_save_password_checkbutton' + widget_name_add) + gpg_password_entry = self.xml.get_widget('gpg_password_entry' + \ + widget_name_add) + + if not keyid or not gajim.config.get('usegpg'): + gpg_save_password_checkbutton.set_sensitive(False) + gpg_password_entry.set_sensitive(False) + gpg_key_label.set_text(_('No key selected')) + gpg_name_label.set_text('') + return + + gpg_key_label.set_text(keyid) + gpg_name_label.set_text(keyname) + gpg_save_password_checkbutton.set_sensitive(True) + gpg_save_password_checkbutton.set_active(savegpgpass) + + if savegpgpass: + gpg_password_entry.set_sensitive(True) + gpgpassword = gajim.config.get_per('accounts', account, 'gpgpassword') + gpg_password_entry.set_text(gpgpassword) + + def init_normal_account(self): + account = self.current_account + # Account tab + jid = gajim.config.get_per('accounts', account, 'name') \ + + '@' + gajim.config.get_per('accounts', account, 'hostname') + self.xml.get_widget('jid_entry1').set_text(jid) + savepass = gajim.config.get_per('accounts', account, 'savepass') + self.xml.get_widget('save_password_checkbutton1').set_active(savepass) + password_entry = self.xml.get_widget('password_entry1') + if savepass: + passstr = passwords.get_password(account) or '' + password_entry.set_sensitive(True) + else: + passstr = '' + password_entry.set_sensitive(False) + password_entry.set_text(passstr) + + self.xml.get_widget('resource_entry1').set_text(gajim.config.get_per( + 'accounts', account, 'resource')) + self.xml.get_widget('adjust_priority_with_status_checkbutton1').\ + set_active(gajim.config.get_per('accounts', account, + 'adjust_priority_with_status')) + spinbutton = self.xml.get_widget('priority_spinbutton1') + if gajim.config.get('enable_negative_priority'): + spinbutton.set_range(-128, 127) + else: + spinbutton.set_range(0, 127) + spinbutton.set_value(gajim.config.get_per('accounts', account, + 'priority')) + + # Connection tab + usessl = gajim.config.get_per('accounts', account, 'usessl') + self.xml.get_widget('use_ssl_checkbutton1').set_active(usessl) + + self.xml.get_widget('send_keepalive_checkbutton1').set_active( + gajim.config.get_per('accounts', account, 'keep_alives_enabled')) + + use_custom_host = gajim.config.get_per('accounts', account, + 'use_custom_host') + self.xml.get_widget('custom_host_port_checkbutton1').set_active( + use_custom_host) + custom_host = gajim.config.get_per('accounts', account, 'custom_host') + if not custom_host: + custom_host = gajim.config.get_per('accounts', account, 'hostname') + self.xml.get_widget('custom_host_entry1').set_text(custom_host) + custom_port = gajim.config.get_per('accounts', account, 'custom_port') + if not custom_port: + custom_port = 5222 + self.xml.get_widget('custom_port_entry1').set_text(unicode(custom_port)) + + # Personal tab + gpg_key_label = self.xml.get_widget('gpg_key_label1') + if gajim.config.get('usegpg'): + self.xml.get_widget('gpg_choose_button1').set_sensitive(True) + self.init_account_gpg() + else: + gpg_key_label.set_text(_('OpenPGP is not usable in this computer')) + self.xml.get_widget('gpg_choose_button1').set_sensitive(False) + + # General tab + self.xml.get_widget('autoconnect_checkbutton1').set_active(gajim.config.\ + get_per('accounts', account, 'autoconnect')) + self.xml.get_widget('autoreconnect_checkbutton1').set_active(gajim. + config.get_per('accounts', account, 'autoreconnect')) + + list_no_log_for = gajim.config.get_per('accounts', account, + 'no_log_for').split() + if account in list_no_log_for: + self.xml.get_widget('log_history_checkbutton1').set_active(False) + else: + self.xml.get_widget('log_history_checkbutton1').set_active(True) + + self.xml.get_widget('sync_with_global_status_checkbutton1').set_active( + gajim.config.get_per('accounts', account, 'sync_with_global_status')) + self.xml.get_widget('use_ft_proxies_checkbutton1').set_active( + gajim.config.get_per('accounts', account, 'use_ft_proxies')) + + def on_add_button_clicked(self, widget): + '''When add button is clicked: open an account information window''' if gajim.interface.instances.has_key('account_creation_wizard'): gajim.interface.instances['account_creation_wizard'].window.present() else: @@ -1907,27 +1688,17 @@ class AccountsWindow: def on_remove_button_clicked(self, widget): '''When delete button is clicked: Remove an account from the listStore and from the config file''' - sel = self.accounts_treeview.get_selection() - (model, iter) = sel.get_selected() - if not iter: + if not self.current_account: return - account = model.get_value(iter, 0).decode('utf-8') + account = self.current_account if len(gajim.events.get_events(account)): dialogs.ErrorDialog(_('Unread events'), _('Read all pending events before removing this account.')) return if gajim.config.get_per('accounts', account, 'is_zeroconf'): - w = self.xml.get_widget('enable_zeroconf_checkbutton') - w.set_active(False) + # Should never happen as button is insensitive return - else: - if gajim.interface.instances[account].has_key('remove_account'): - gajim.interface.instances[account]['remove_account'].window.present( - ) - else: - gajim.interface.instances[account]['remove_account'] = \ - RemoveAccountWindow(account) win_opened = False if gajim.interface.msg_win_mgr.get_controls(acct = account): @@ -1958,39 +1729,417 @@ class AccountsWindow: else: remove(widget, account) - def on_modify_button_clicked(self, widget): - '''When modify button is clicked: - open/show the account modification window for this account''' - sel = self.accounts_treeview.get_selection() - (model, iter) = sel.get_selected() - if not iter: + def on_rename_button_clicked(self, widget): + if gajim.connections[self.current_account].connected != 0: + dialogs.ErrorDialog( + _('You are currently connected to the server'), + _('To change the account name, you must be disconnected.')) return - account = model[iter][0].decode('utf-8') - self.show_modification_window(account) + if len(gajim.events.get_events(self.current_account)): + dialogs.ErrorDialog(_('Unread events'), + _('To change the account name, you must read all pending ' + 'events.')) + return + # Get the new name + def on_renamed(new_name, old_name): + if new_name in gajim.connections: + dialogs.ErrorDialog(_('Account Name Already Used'), + _('This name is already used by another of your accounts. ' + 'Please choose another name.')) + return + if (new_name == ''): + dialogs.ErrorDialog(_('Invalid account name'), + _('Account name cannot be empty.')) + return + if new_name.find(' ') != -1: + dialogs.ErrorDialog(_('Invalid account name'), + _('Account name cannot contain spaces.')) + return + # update variables + gajim.interface.instances[new_name] = gajim.interface.instances[ + old_name] + gajim.interface.minimized_controls[new_name] = \ + gajim.interface.minimized_controls[old_name] + gajim.nicks[new_name] = gajim.nicks[old_name] + gajim.block_signed_in_notifications[new_name] = \ + gajim.block_signed_in_notifications[old_name] + gajim.groups[new_name] = gajim.groups[old_name] + gajim.gc_connected[new_name] = gajim.gc_connected[old_name] + gajim.automatic_rooms[new_name] = gajim.automatic_rooms[old_name] + gajim.newly_added[new_name] = gajim.newly_added[old_name] + gajim.to_be_removed[new_name] = gajim.to_be_removed[old_name] + gajim.sleeper_state[new_name] = gajim.sleeper_state[old_name] + gajim.encrypted_chats[new_name] = gajim.encrypted_chats[old_name] + gajim.last_message_time[new_name] = gajim.last_message_time[old_name] + gajim.status_before_autoaway[new_name] = \ + gajim.status_before_autoaway[old_name] + gajim.transport_avatar[new_name] = gajim.transport_avatar[old_name] - def on_accounts_treeview_row_activated(self, widget, path, column): - model = widget.get_model() - account = model[path][0].decode('utf-8') - self.show_modification_window(account) + gajim.contacts.change_account_name(old_name, new_name) + gajim.events.change_account_name(old_name, new_name) - def show_modification_window(self, account): - if gajim.config.get_per('accounts', account, 'is_zeroconf'): - if gajim.interface.instances.has_key('zeroconf_properties'): - gajim.interface.instances['zeroconf_properties'].window.present() - else: - gajim.interface.instances['zeroconf_properties'] = \ - ZeroconfPropertiesWindow() + # change account variable for chat / gc controls + gajim.interface.msg_win_mgr.change_account_name(old_name, new_name) + # upgrade account variable in opened windows + for kind in ('infos', 'disco', 'gc_config', 'search'): + for j in gajim.interface.instances[new_name][kind]: + gajim.interface.instances[new_name][kind][j].account = new_name + + # ServiceCache object keep old property account + if hasattr(gajim.connections[old_name], 'services_cache'): + gajim.connections[self.account].services_cache.account = new_name + del gajim.interface.instances[old_name] + del gajim.interface.minimized_controls[old_name] + del gajim.nicks[old_name] + del gajim.block_signed_in_notifications[old_name] + del gajim.groups[old_name] + del gajim.gc_connected[old_name] + del gajim.automatic_rooms[old_name] + del gajim.newly_added[old_name] + del gajim.to_be_removed[old_name] + del gajim.sleeper_state[old_name] + del gajim.encrypted_chats[old_name] + del gajim.last_message_time[old_name] + del gajim.status_before_autoaway[old_name] + del gajim.transport_avatar[old_name] + gajim.connections[old_name].name = new_name + gajim.connections[new_name] = gajim.connections[old_name] + del gajim.connections[old_name] + gajim.config.add_per('accounts', new_name) + old_config = gajim.config.get_per('accounts', old_name) + for opt in old_config: + gajim.config.set_per('accounts', new_name, opt, old_config[opt][1]) + gajim.config.del_per('accounts', old_name) + if self.current_account == old_name: + self.current_account = new_name + # refresh roster + gajim.interface.roster.draw_roster() + self.init_accounts() + self.select_account(new_name) + + title = _('Rename Account') + message = _('Enter a new name for account %s') % self.current_account + old_text = self.current_account + dialogs.InputDialog(title, message, old_text, is_modal=False, + ok_handler=(on_renamed, self.current_account)) + + def option_changed(self, option, value): + return gajim.config.get_per('accounts', self.current_account, option) != \ + value + + def on_jid_entry1_focus_out_event(self, widget, event): + if self.ignore_events: + return + jid = widget.get_text() + # check if jid is conform to RFC and stringprep it + try: + jid = helpers.parse_jid(jid) + except helpers.InvalidFormat, s: + if not widget.is_focus(): + pritext = _('Invalid Jabber ID') + dialogs.ErrorDialog(pritext, str(s)) + gobject.idle_add(lambda: widget.grab_focus()) + return True + + jid_splited = jid.split('@', 1) + if len(jid_splited) != 2: + if not widget.is_focus(): + pritext = _('Invalid Jabber ID') + sectext = _('A Jabber ID must be in the form "user@servername".') + dialogs.ErrorDialog(pritext, sectext) + gobject.idle_add(lambda: widget.grab_focus()) + return True + + if self.option_changed('name', jid_splited[0]) or \ + self.option_changed('hostname', jid_splited[1]): + self.need_relogin = True + + gajim.config.set_per('accounts', self.current_account, 'name', + jid_splited[0]) + gajim.config.set_per('accounts', self.current_account, 'hostname', + jid_splited[1]) + + def on_password_entry1_changed(self, widget): + if self.ignore_events: + return + passwords.save_password(self.current_account, widget.get_text()) + + def on_save_password_checkbutton1_toggled(self, widget): + if self.ignore_events: + return + active = widget.get_active() + gajim.config.set_per('accounts', self.current_account, 'savepass', active) + if active: + password = self.xml.get_widget('password_entry1').get_text() + passwords.save_password(self.current_account, password) else: - if gajim.interface.instances[account].has_key('account_modification'): - gajim.interface.instances[account]['account_modification'].window.\ - present() + passwords.save_password(self.current_account, '') + + def on_resource_entry1_focus_out_event(self, widget, event): + if self.ignore_events: + return + resource = self.xml.get_widget('resource_entry1').get_text().decode( + 'utf-8') + try: + resource = helpers.parse_resource(resource) + except helpers.InvalidFormat, s: + if not widget.is_focus(): + pritext = _('Invalid Jabber ID') + dialogs.ErrorDialog(pritext, str(s)) + gobject.idle_add(lambda: widget.grab_focus()) + return True + + if self.option_changed('resource', resource): + self.need_relogin = True + + gajim.config.set_per('accounts', self.current_account, 'resource', + resource) + + def on_adjust_priority_with_status_checkbutton1_toggled(self, widget): + self.xml.get_widget('priority_spinbutton1').set_sensitive( + not widget.get_active()) + self.on_checkbutton_toggled(widget, 'adjust_priority_with_status', + account = self.current_account) + + def on_priority_spinbutton1_value_changed(self, widget): + prio = widget.get_value_as_int() + + if self.option_changed('priority', prio): + self.resend_presence = True + + gajim.config.set_per('accounts', self.current_account, 'priority', prio) + + def on_synchronise_contacts_button1_clicked(self, widget): + try: + dialog = dialogs.SynchroniseSelectAccountDialog(self.current_account) + except GajimGeneralException: + # If we showed ErrorDialog, there will not be dialog instance + return + + def on_change_password_button1_clicked(self, widget): + try: + dialog = dialogs.ChangePasswordDialog(self.current_account) + except GajimGeneralException: + # if we showed ErrorDialog, there will not be dialog instance + return + + new_password = dialog.run() + if new_password != -1: + gajim.connections[self.current_account].change_password(new_password) + if self.xml.get_widget('save_password_checkbutton1').get_active(): + self.xml.get_widget('password_entry1').set_text(new_password) + + def on_autoconnect_checkbutton_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'autoconnect', + account=self.current_account) + + def on_autoreconnect_checkbutton_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'autoreconnect', + account=self.current_account) + + def on_log_history_checkbutton_toggled(self, widget): + if self.ignore_events: + return + list_no_log_for = gajim.config.get_per('accounts', self.current_account, + 'no_log_for').split() + if self.current_account in list_no_log_for: + list_no_log_for.remove(self.current_account) + if not self.xml.get_widget('log_history_checkbutton').get_active(): + list_no_log_for.append(self.current_account) + gajim.config.set_per('accounts', self.current_account, 'no_log_for', + ' '.join(list_no_log_for)) + + def on_sync_with_global_status_checkbutton_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'sync_with_global_status', + account=self.current_account) + + def on_use_ft_proxies_checkbutton1_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'use_ft_proxies', + account=self.current_account) + + def on_proxies_combobox1_changed(self, widget): + active = widget.get_active() + proxy = widget.get_model()[active][0].decode('utf-8') + if proxy == _('None'): + proxy = '' + + if self.option_changed('proxy', proxy): + self.need_relogin = True + + gajim.config.set_per('accounts', self.current_account, 'proxy', proxy) + + def on_manage_proxies_button1_clicked(self, widget): + if gajim.interface.instances.has_key('manage_proxies'): + gajim.interface.instances['manage_proxies'].window.present() + else: + gajim.interface.instances['manage_proxies'] = ManageProxiesWindow() + + def on_use_ssl_checkbutton1_toggled(self, widget): + if self.ignore_events: + return + + if self.option_changed('usessl', widget.get_active()): + self.need_relogin = True + + isactive = widget.get_active() + if isactive: + self.xml.get_widget('custom_port_entry1').set_text('5223') + else: + self.xml.get_widget('custom_port_entry1').set_text('5222') + + self.on_checkbutton_toggled(widget, 'usessl', + account=self.current_account) + + def on_send_keepalive_checkbutton1_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'keep_alives_enabled', + account=self.current_account) + + def on_custom_host_port_checkbutton1_toggled(self, widget): + if self.option_changed('use_custom_host', widget.get_active()): + self.need_relogin = True + + self.on_checkbutton_toggled(widget, 'use_custom_host', + account=self.current_account) + active = widget.get_active() + self.xml.get_widget('custom_host_port_hbox1').set_sensitive(active) + + def on_custom_host_entry1_changed(self, widget): + if self.ignore_events: + return + host = widget.get_text().decode('utf-8') + if self.option_changed('custom_host', host): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, 'custom_host', + host) + + def on_custom_port_entry_focus_out_event(self, widget, event): + if self.ignore_events: + return + custom_port = widget.get_text() + try: + custom_port = int(custom_port) + except: + if not widget.is_focus(): + dialogs.ErrorDialog(_('Invalid entry'), + _('Custom port must be a port number.')) + gobject.idle_add(lambda: widget.grab_focus()) + return True + if self.option_changed('custom_port', custom_port): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, 'custom_port', + custom_port) + + def on_gpg_choose_button_clicked(self, widget, data = None): + if gajim.connections.has_key(self.current_account): + secret_keys = gajim.connections[self.current_account].\ + ask_gpg_secrete_keys() + + # self.current_account is None and/or gajim.connections is {} + else: + from common import GnuPG + if GnuPG.USE_GPG: + secret_keys = GnuPG.GnuPG().get_secret_keys() else: - gajim.interface.instances[account]['account_modification'] = \ - AccountModificationWindow(account) + secret_keys = [] + if not secret_keys: + dialogs.ErrorDialog(_('Failed to get secret keys'), + _('There was a problem retrieving your OpenPGP secret keys.')) + return + secret_keys[_('None')] = _('None') + instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'), + _('Choose your OpenPGP key'), secret_keys) + keyID = instance.run() + if keyID is None: + return + if self.current_account == gajim.ZEROCONF_ACC_NAME: + wiget_name_ext = '2' + else: + wiget_name_ext = '1' + checkbutton = self.xml.get_widget('gpg_save_password_checkbutton' + \ + wiget_name_ext) + gpg_key_label = self.xml.get_widget('gpg_key_label' + wiget_name_ext) + gpg_name_label = self.xml.get_widget('gpg_name_label' + wiget_name_ext) + gpg_password_entry = self.xml.get_widget('gpg_password_entry' + \ + wiget_name_ext) + if keyID[0] == _('None'): + gpg_key_label.set_text(_('No key selected')) + gpg_name_label.set_text('') + checkbutton.set_sensitive(False) + gpg_password_entry.set_sensitive(False) + if self.option_changed('keyid', ''): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, 'keyname', '') + gajim.config.set_per('accounts', self.current_account, 'keyid', '') + else: + gpg_key_label.set_text(keyID[0]) + gpg_name_label.set_text(keyID[1]) + checkbutton.set_sensitive(True) + if self.option_changed('keyid', keyID[0]): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, 'keyname', + keyID[1]) + gajim.config.set_per('accounts', self.current_account, 'keyid', + keyID[0]) + gajim.config.set_per('accounts', self.current_account, 'savegpgpass', + False) + gajim.config.set_per('accounts', self.current_account, 'gpgpassword', '') + checkbutton.set_active(False) + gpg_password_entry.set_text('') + + def on_gpg_save_password_checkbutton_toggled(self, widget): + if self.current_account == gajim.ZEROCONF_ACC_NAME: + wiget_name_ext = '2' + else: + wiget_name_ext = '1' + self.xml.get_widget('gpg_password_entry' + wiget_name_ext).set_sensitive( + widget.get_active()) + self.on_checkbutton_toggled(widget, 'savegpgpass', + account = self.current_account) + + def on_gpg_password_entry_changed(self, widget): + if self.ignore_events: + return + gajim.config.set_per('accounts', self.current_account, 'gpgpassword', + widget.get_text().decode('utf-8')) + + def on_edit_details_button1_clicked(self, widget): + if not gajim.interface.instances.has_key(self.current_account): + dialogs.ErrorDialog(_('No such account available'), + _('You must create your account before editing your personal ' + 'information.')) + return + + # show error dialog if account is newly created (not in gajim.connections) + if not gajim.connections.has_key(self.current_account) or \ + gajim.connections[self.current_account].connected < 2: + dialogs.ErrorDialog(_('You are not connected to the server'), + _('Without a connection, you can not edit your personal information.')) + return + + if not gajim.connections[self.current_account].vcard_supported: + dialogs.ErrorDialog(_("Your server doesn't support Vcard"), + _("Your server can't save your personal information.")) + return + + gajim.interface.edit_own_details(self.current_account) def on_checkbutton_toggled(self, widget, config_name, - change_sensitivity_widgets = None): - gajim.config.set(config_name, widget.get_active()) + change_sensitivity_widgets = None, account = None): + if account: + gajim.config.set_per('accounts', account, config_name, + widget.get_active()) + else: + gajim.config.set(config_name, widget.get_active()) if change_sensitivity_widgets: for w in change_sensitivity_widgets: w.set_sensitive(widget.get_active()) @@ -2004,8 +2153,7 @@ class AccountsWindow: gajim.interface.roster.regroup = False gajim.interface.roster.draw_roster() - - def on_enable_zeroconf_checkbutton_toggled(self, widget): + def on_enable_zeroconf_checkbutton2_toggled(self, widget): # don't do anything if there is an account with the local name but is a # normal account if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME) and not \ @@ -2014,19 +2162,17 @@ class AccountsWindow: (_('Account Local already exists.'), _('Please rename or remove it before enabling link-local messaging' '.'))) - widget.disconnect(self.zeroconf_toggled_id) - widget.set_active(False) - self.zeroconf_toggled_id = widget.connect('toggled', - self.on_enable_zeroconf_checkbutton_toggled) return - if gajim.config.get('enable_zeroconf'): - #disable + if gajim.config.get('enable_zeroconf') and not widget.get_active(): + self.xml.get_widget('zeroconf_notebook').set_sensitive(False) + # disable gajim.interface.roster.close_all(gajim.ZEROCONF_ACC_NAME) gajim.connections[gajim.ZEROCONF_ACC_NAME].disable_account() del gajim.connections[gajim.ZEROCONF_ACC_NAME] gajim.interface.save_config() del gajim.interface.instances[gajim.ZEROCONF_ACC_NAME] + del gajim.interface.minimized_controls[gajim.ZEROCONF_ACC_NAME] del gajim.nicks[gajim.ZEROCONF_ACC_NAME] del gajim.block_signed_in_notifications[gajim.ZEROCONF_ACC_NAME] del gajim.groups[gajim.ZEROCONF_ACC_NAME] @@ -2047,16 +2193,16 @@ class AccountsWindow: gajim.interface.roster.regroup = False gajim.interface.roster.draw_roster() gajim.interface.roster.actions_menu_needs_rebuild = True - if gajim.interface.instances.has_key('accounts'): - gajim.interface.instances['accounts'].init_accounts() - - else: + + elif not gajim.config.get('enable_zeroconf') and widget.get_active(): + self.xml.get_widget('zeroconf_notebook').set_sensitive(True) # enable (will create new account if not present) gajim.connections[gajim.ZEROCONF_ACC_NAME] = common.zeroconf.\ connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME) # update variables gajim.interface.instances[gajim.ZEROCONF_ACC_NAME] = {'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {}} + gajim.interface.minimized_controls[gajim.ZEROCONF_ACC_NAME] = {} gajim.connections[gajim.ZEROCONF_ACC_NAME].connected = 0 gajim.groups[gajim.ZEROCONF_ACC_NAME] = {} gajim.contacts.add_account(gajim.ZEROCONF_ACC_NAME) @@ -2071,9 +2217,6 @@ class AccountsWindow: gajim.last_message_time[gajim.ZEROCONF_ACC_NAME] = {} gajim.status_before_autoaway[gajim.ZEROCONF_ACC_NAME] = '' gajim.transport_avatar[gajim.ZEROCONF_ACC_NAME] = {} - # refresh accounts window - if gajim.interface.instances.has_key('accounts'): - gajim.interface.instances['accounts'].init_accounts() # refresh roster if len(gajim.connections) >= 2: # Do not merge accounts if only one exists @@ -2083,10 +2226,53 @@ class AccountsWindow: gajim.interface.roster.draw_roster() gajim.interface.roster.actions_menu_needs_rebuild = True gajim.interface.save_config() - gajim.connections[gajim.ZEROCONF_ACC_NAME].change_status('online', '') self.on_checkbutton_toggled(widget, 'enable_zeroconf') + def on_custom_port_checkbutton2_toggled(self, widget): + self.xml.get_widget('custom_port_entry2').set_sensitive( + widget.get_active()) + self.on_checkbutton_toggled(widget, 'use_custom_host', + account = self.current_account) + if not widget.get_active(): + self.xml.get_widget('custom_port_entry2').set_text('5298') + + def on_first_name_entry2_changed(self, widget): + if self.ignore_events: + return + name = widget.get_text().decode('utf-8') + if self.option_changed('zeroconf_first_name', name): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, + 'zeroconf_first_name', name) + + def on_last_name_entry2_changed(self, widget): + if self.ignore_events: + return + name = widget.get_text().decode('utf-8') + if self.option_changed('zeroconf_last_name', name): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, + 'zeroconf_last_name', name) + + def on_jabber_id_entry2_changed(self, widget): + if self.ignore_events: + return + id = widget.get_text().decode('utf-8') + if self.option_changed('zeroconf_jabber_id', id): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, + 'zeroconf_jabber_id', id) + + def on_email_entry2_changed(self, widget): + if self.ignore_events: + return + email = widget.get_text().decode('utf-8') + if self.option_changed('zeroconf_email', email): + self.need_relogin = True + gajim.config.set_per('accounts', self.current_account, + 'zeroconf_email', email) + class FakeDataForm(gtk.Table, object): '''Class for forms that are in XML format value1 infos in a table {entry1: value1, }''' @@ -2449,6 +2635,7 @@ class RemoveAccountWindow: gajim.config.del_per('accounts', self.account) gajim.interface.save_config() del gajim.interface.instances[self.account] + del gajim.interface.minimized_controls[self.account] del gajim.nicks[self.account] del gajim.block_signed_in_notifications[self.account] del gajim.groups[self.account] @@ -2479,8 +2666,9 @@ class ManageBookmarksWindow: self.window = self.xml.get_widget('manage_bookmarks_window') self.window.set_transient_for(gajim.interface.roster.window) - # Account-JID, RoomName, Room-JID, Autojoin, Passowrd, Nick, Show_Status - self.treestore = gtk.TreeStore(str, str, str, bool, str, str, str) + # Account-JID, RoomName, Room-JID, Autojoin, Minimize, Passowrd, Nick, + # Show_Status + self.treestore = gtk.TreeStore(str, str, str, bool, bool, str, str, str) # Store bookmarks in treeview. for account in gajim.connections: @@ -2488,7 +2676,7 @@ class ManageBookmarksWindow: continue if gajim.connections[account].is_zeroconf: continue - iter = self.treestore.append(None, [None, account,None, + iter = self.treestore.append(None, [None, account, None, None, None, None, None, None]) for bookmark in gajim.connections[account].bookmarks: @@ -2502,6 +2690,9 @@ class ManageBookmarksWindow: autojoin = helpers.from_xs_boolean_to_python_boolean( bookmark['autojoin']) + minimize = helpers.from_xs_boolean_to_python_boolean( + bookmark['minimize']) + print_status = bookmark.get('print_status', '') if print_status not in ('', 'all', 'in_and_out', 'none'): print_status = '' @@ -2510,6 +2701,7 @@ class ManageBookmarksWindow: bookmark['name'], bookmark['jid'], autojoin, + minimize, bookmark['password'], bookmark['nick'], print_status ]) @@ -2551,6 +2743,7 @@ class ManageBookmarksWindow: self.pass_entry = self.xml.get_widget('pass_entry') self.pass_entry.connect('changed', self.on_pass_entry_changed) self.autojoin_checkbutton = self.xml.get_widget('autojoin_checkbutton') + self.minimize_checkbutton = self.xml.get_widget('minimize_checkbutton') self.xml.signal_autoconnect(self) self.window.show_all() @@ -2588,10 +2781,10 @@ class ManageBookmarksWindow: account = model[add_to][1].decode('utf-8') nick = gajim.nicks[account] iter_ = self.treestore.append(add_to, [account, _('New Group Chat'), '', - False, '', nick, 'in_and_out']) - self.view.set_cursor(model.get_path(iter_)) + False, False, '', nick, 'in_and_out']) self.view.expand_row(model.get_path(add_to), True) + self.view.set_cursor(model.get_path(iter_)) def on_remove_bookmark_button_clicked(self, widget): ''' @@ -2645,10 +2838,12 @@ class ManageBookmarksWindow: for bm in account.iterchildren(): #Convert True/False/None to '1' or '0' autojoin = unicode(int(bm[3])) + minimize = unicode(int(bm[4])) #create the bookmark-dict bmdict = { 'name': bm[1], 'jid': bm[2], 'autojoin': autojoin, - 'password': bm[4], 'nick': bm[5], 'print_status': bm[6]} + 'minimize': minimize, 'password': bm[5], 'nick': bm[6], + 'print_status': bm[7]} gajim.connections[account_unicode].bookmarks.append(bmdict) @@ -2672,7 +2867,7 @@ class ManageBookmarksWindow: widgets = [ self.title_entry, self.nick_entry, self.room_entry, self.server_entry, self.pass_entry, self.autojoin_checkbutton, - self.print_status_combobox] + self.minimize_checkbutton, self.print_status_combobox] if model.iter_parent(iter): # make the fields sensitive @@ -2699,8 +2894,12 @@ class ManageBookmarksWindow: self.server_entry.set_text(server) self.autojoin_checkbutton.set_active(model[iter][3]) - if model[iter][4] is not None: - password = model[iter][4].decode('utf-8') + self.minimize_checkbutton.set_active(model[iter][4]) + # sensitive only if auto join is checked + self.minimize_checkbutton.set_sensitive(model[iter][3]) + + if model[iter][5] is not None: + password = model[iter][5].decode('utf-8') else: password = None @@ -2708,14 +2907,14 @@ class ManageBookmarksWindow: self.pass_entry.set_text(password) else: self.pass_entry.set_text('') - nick = model[iter][5] + nick = model[iter][6] if nick: nick = nick.decode('utf-8') self.nick_entry.set_text(nick) else: self.nick_entry.set_text('') - print_status = model[iter][6] + print_status = model[iter][7] opts = self.option_list.keys() opts.sort() self.print_status_combobox.set_active(opts.index(print_status)) @@ -2730,7 +2929,7 @@ class ManageBookmarksWindow: def on_nick_entry_changed(self, widget): (model, iter) = self.selection.get_selected() if iter: - model[iter][5] = self.nick_entry.get_text() + model[iter][6] = self.nick_entry.get_text() def on_server_entry_changed(self, widget): (model, iter) = self.selection.get_selected() @@ -2749,12 +2948,18 @@ class ManageBookmarksWindow: def on_pass_entry_changed(self, widget): (model, iter) = self.selection.get_selected() if iter: - model[iter][4] = self.pass_entry.get_text() + model[iter][5] = self.pass_entry.get_text() def on_autojoin_checkbutton_toggled(self, widget): (model, iter) = self.selection.get_selected() if iter: model[iter][3] = self.autojoin_checkbutton.get_active() + self.minimize_checkbutton.set_sensitive(model[iter][3]) + + def on_minimize_checkbutton_toggled(self, widget): + (model, iter) = self.selection.get_selected() + if iter: + model[iter][4] = self.minimize_checkbutton.get_active() def on_print_status_combobox_changed(self, widget): active = widget.get_active() @@ -2762,7 +2967,7 @@ class ManageBookmarksWindow: print_status = model[active][1] (model2, iter) = self.selection.get_selected() if iter: - model2[iter][6] = print_status + model2[iter][7] = print_status def clear_fields(self): widgets = [ self.title_entry, self.nick_entry, self.room_entry, @@ -2770,6 +2975,7 @@ class ManageBookmarksWindow: for field in widgets: field.set_text('') self.autojoin_checkbutton.set_active(False) + self.minimize_checkbutton.set_active(False) self.print_status_combobox.set_active(1) class AccountCreationWizardWindow: @@ -2829,8 +3035,6 @@ class AccountCreationWizardWindow: self.update_progressbar_timeout_id = None self.notebook.set_current_page(0) - self.advanced_button.set_no_show_all(True) - self.finish_button.set_no_show_all(True) self.xml.signal_autoconnect(self) self.window.show_all() @@ -2842,7 +3046,7 @@ class AccountCreationWizardWindow: 'http://www.jabber.org/network/oldnetwork.shtml') def on_save_password_checkbutton_toggled(self, widget): - self.xml.get_widget('pass1_entry').grab_focus() + self.xml.get_widget('password_entry').grab_focus() def on_cancel_button_clicked(self, widget): self.window.destroy() @@ -3099,8 +3303,12 @@ class AccountCreationWizardWindow: gobject.source_remove(self.update_progressbar_timeout_id) def on_advanced_button_clicked(self, widget): - gajim.interface.instances[self.account]['account_modification'] = \ - AccountModificationWindow(self.account) + if gajim.interface.instances.has_key('accounts'): + gajim.interface.instances['accounts'].window.present() + else: + gajim.interface.instances['accounts'] = AccountsWindow() + gajim.interface.instances['accounts'].select_account( + self.account) self.window.destroy() def on_finish_button_clicked(self, widget): @@ -3198,6 +3406,7 @@ class AccountCreationWizardWindow: # update variables gajim.interface.instances[self.account] = {'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {}} + gajim.interface.minimized_controls[self.account] = {} gajim.connections[self.account].connected = 0 gajim.groups[self.account] = {} gajim.contacts.add_account(self.account) diff --git a/src/conversation_textview.py b/src/conversation_textview.py index d3ac1e875..f4e06e1bb 100644 --- a/src/conversation_textview.py +++ b/src/conversation_textview.py @@ -26,6 +26,7 @@ import os import tooltips import dialogs import locale +import Queue import gtkgui_helpers from common import gajim @@ -141,9 +142,14 @@ class ConversationTextview: buffer.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER) + # One mark at the begining then 2 marks between each lines + size = gajim.config.get('max_conversation_lines') + size = 2 * size - 1 + self.marks_queue = Queue.Queue(size) + self.allow_focus_out_line = True - # holds the iter's offset which points to the end of --- line - self.focus_out_end_iter_offset = None + # holds a mark at the end of --- line + self.focus_out_end_mark = None self.line_tooltip = tooltips.BaseTooltip() # use it for hr too @@ -213,13 +219,14 @@ class ConversationTextview: print_focus_out_line = False buffer = self.tv.get_buffer() - if self.focus_out_end_iter_offset is None: + if self.focus_out_end_mark is None: # this happens only first time we focus out on this room print_focus_out_line = True else: - if self.focus_out_end_iter_offset != buffer.get_end_iter().\ - get_offset(): + focus_out_end_iter = buffer.get_iter_at_mark(self.focus_out_end_mark) + focus_out_end_iter_offset = focus_out_end_iter.get_offset() + if focus_out_end_iter_offset != buffer.get_end_iter().get_offset(): # this means after last-focus something was printed # (else end_iter's offset is the same as before) # only then print ---- line (eg. we avoid printing many following @@ -230,9 +237,9 @@ class ConversationTextview: buffer.begin_user_action() # remove previous focus out line if such focus out line exists - if self.focus_out_end_iter_offset is not None: - end_iter_for_previous_line = buffer.get_iter_at_offset( - self.focus_out_end_iter_offset) + if self.focus_out_end_mark is not None: + end_iter_for_previous_line = buffer.get_iter_at_mark( + self.focus_out_end_mark) begin_iter_for_previous_line = end_iter_for_previous_line.copy() # img_char+1 (the '\n') begin_iter_for_previous_line.backward_chars(2) @@ -240,6 +247,7 @@ class ConversationTextview: # remove focus out line buffer.delete(begin_iter_for_previous_line, end_iter_for_previous_line) + buffer.delete_mark(self.focus_out_end_mark) # add the new focus out line end_iter = buffer.get_end_iter() @@ -255,7 +263,8 @@ class ConversationTextview: self.allow_focus_out_line = False # update the iter we hold to make comparison the next time - self.focus_out_end_iter_offset = buffer.get_end_iter().get_offset() + self.focus_out_end_mark = buffer.create_mark(None, + buffer.get_end_iter(), left_gravity=True) buffer.end_user_action() @@ -315,7 +324,10 @@ class ConversationTextview: buffer = self.tv.get_buffer() start, end = buffer.get_bounds() buffer.delete(start, end) - self.focus_out_end_iter_offset = None + size = gajim.config.get('max_conversation_lines') + size = 2 * size - 1 + self.marks_queue = Queue.Queue(size) + self.focus_out_end_mark = None def visit_url_from_menuitem(self, widget, link): '''basically it filters out the widget instance''' @@ -767,13 +779,29 @@ class ConversationTextview: '''prints 'chat' type messages''' buffer = self.tv.get_buffer() buffer.begin_user_action() + if self.marks_queue.full(): + # remove oldest line + m1 = self.marks_queue.get() + m2 = self.marks_queue.get() + i1 = buffer.get_iter_at_mark(m1) + i2 = buffer.get_iter_at_mark(m2) + buffer.delete(i1, i2) + buffer.delete_mark(m1) end_iter = buffer.get_end_iter() at_the_end = False if self.at_the_end(): at_the_end = True + # Create one mark and add it to queue once if it's the first line + # else twice (one for end bound, one for start bound) + mark = None if buffer.get_char_count() > 0: buffer.insert_with_tags_by_name(end_iter, '\n', 'eol') + mark = buffer.create_mark(None, end_iter, left_gravity=True) + self.marks_queue.put(mark) + if not mark: + mark = buffer.create_mark(None, end_iter, left_gravity=True) + self.marks_queue.put(mark) if kind == 'incoming_queue': kind = 'incoming' if old_kind == 'incoming_queue': diff --git a/src/dialogs.py b/src/dialogs.py index 4d7cc0a3c..c264adbcd 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -8,6 +8,7 @@ ## Copyright (C) 2005-2006 Travis Shirk ## Copyright (C) 2005 Norman Rasmussen ## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -115,7 +116,7 @@ class EditGroupsDialog: if not gajim.interface.roster.regroup and _account != account: continue for _jid in all_jid[_account]: - contacts = gajim.contacts.get_contact(_account, _jid) + contacts = gajim.contacts.get_contacts(_account, _jid) for c in contacts: if group in c.groups: c.groups.remove(group) @@ -133,7 +134,7 @@ class EditGroupsDialog: if not gajim.interface.roster.regroup and _account != account: continue for _jid in all_jid[_account]: - contacts = gajim.contacts.get_contact(_account, _jid) + contacts = gajim.contacts.get_contacts(_account, _jid) for c in contacts: if not group in c.groups: c.groups.append(group) @@ -547,13 +548,20 @@ class ChangeStatusMessageDialog: if not msg_name: # msg_name was '' msg_name = msg_text_1l msg_name = msg_name.decode('utf-8') - iter_ = self.message_liststore.append((msg_name,)) - gajim.config.add_per('statusmsg', msg_name) + if msg_name in self.preset_messages_dict: + dlg2 = ConfirmationDialog(_('Overwrite Status Message?'), + _('This name is already used. Do you want to overwrite this status message?')) + resp = dlg2.run() + if resp != gtk.RESPONSE_OK: + return + else: + iter_ = self.message_liststore.append((msg_name,)) + gajim.config.add_per('statusmsg', msg_name) + # select in combobox the one we just saved + self.message_combobox.set_active_iter(iter_) gajim.config.set_per('statusmsg', msg_name, 'message', msg_text_1l) self.preset_messages_dict[msg_name] = msg_text - # select in combobox the one we just saved - self.message_combobox.set_active_iter(iter_) class AddNewContactWindow: @@ -610,6 +618,8 @@ _('Please fill in the data of the contact you want to add in account %s') %accou for j in gajim.contacts.get_jid_list(acct): if gajim.jid_is_transport(j): type_ = gajim.get_transport_name_from_jid(j, False) + if not type_: + continue if self.agents.has_key(type_): self.agents[type_].append(j) else: @@ -625,30 +635,37 @@ _('Please fill in the data of the contact you want to add in account %s') %accou self.agents[type_].append(jid_) self.available_types.append(type_) liststore = gtk.ListStore(str) - self.group_comboboxentry.set_model(liststore) - liststore = gtk.ListStore(str, str) + self.group_comboboxentry.set_model(liststore) + # Combobox with transport/jabber icons + liststore = gtk.ListStore(str, gtk.gdk.Pixbuf, str) + cell = gtk.CellRendererPixbuf() + self.protocol_combobox.pack_start(cell, False) + self.protocol_combobox.add_attribute(cell, 'pixbuf', 1) + cell = gtk.CellRendererText() + cell.set_property('xpad', 5) + self.protocol_combobox.pack_start(cell, True) + self.protocol_combobox.add_attribute(cell, 'text', 0) + self.protocol_combobox.set_model(liststore) uf_type = {'jabber': 'Jabber', 'aim': 'AIM', 'gadu-gadu': 'Gadu Gadu', 'icq': 'ICQ', 'msn': 'MSN', 'yahoo': 'Yahoo'} # Jabber as first - liststore.append(['Jabber', 'jabber']) + img = gajim.interface.roster.jabber_state_images['16']['online'] + liststore.append(['Jabber', img.get_pixbuf(), 'jabber']) for type_ in self.agents: if type_ == 'jabber': continue - if type_ in uf_type: - liststore.append([uf_type[type_], type_]) + imgs = gajim.interface.roster.transports_state_images + img = None + if imgs['16'].has_key(type_) and imgs['16'][type_].has_key('online'): + img = imgs['16'][type_]['online'] + if type_ in uf_type: + liststore.append([uf_type[type_], img.get_pixbuf(), type_]) + else: + liststore.append([type_, img.get_pixbuf(), type_]) else: - liststore.append([type_, type_]) - self.protocol_combobox.set_model(liststore) + liststore.append([type_, img, type_]) self.protocol_combobox.set_active(0) - self.protocol_jid_combobox.set_no_show_all(True) - self.protocol_jid_combobox.hide() - self.subscription_table.set_no_show_all(True) self.auto_authorize_checkbutton.show() - self.message_scrolledwindow.set_no_show_all(True) - self.register_hbox.set_no_show_all(True) - self.register_hbox.hide() - self.connected_label.set_no_show_all(True) - self.connected_label.hide() liststore = gtk.ListStore(str) self.protocol_jid_combobox.set_model(liststore) self.xml.signal_autoconnect(self) @@ -666,7 +683,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou iter = model.get_iter_first() i = 0 while iter: - if model[iter][1] == type_: + if model[iter][2] == type_: self.protocol_combobox.set_active(i) break iter = model.iter_next(iter) @@ -741,7 +758,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou model = self.protocol_combobox.get_model() iter = self.protocol_combobox.get_active_iter() - type_ = model[iter][1] + type_ = model[iter][2] if type_ != 'jabber': transport = self.protocol_jid_combobox.get_active_text().decode( 'utf-8') @@ -795,7 +812,7 @@ _('Please fill in the data of the contact you want to add in account %s') %accou def on_protocol_combobox_changed(self, widget): model = widget.get_model() iter = widget.get_active_iter() - type_ = model[iter][1] + type_ = model[iter][2] model = self.protocol_jid_combobox.get_model() model.clear() if len(self.agents[type_]): @@ -1192,6 +1209,7 @@ class InputDialog: def on_okbutton_clicked(self, widget): user_input = self.input_entry.get_text().decode('utf-8') + self.cancel_handler = None self.dialog.destroy() if isinstance(self.ok_handler, tuple): self.ok_handler[0](user_input, *self.ok_handler[1:]) @@ -1327,7 +1345,8 @@ class SubscriptionRequestWindow: class JoinGroupchatWindow: - def __init__(self, account, room_jid = '', nick = '', automatic = False): + def __init__(self, account, room_jid = '', nick = '', password = '', + automatic = False): '''automatic is a dict like {'invities': []} If automatic is not empty, this means room must be automaticaly configured and when done, invities must be automatically invited''' @@ -1351,9 +1370,12 @@ class JoinGroupchatWindow: self.window = self.xml.get_widget('join_groupchat_window') self._room_jid_entry = self.xml.get_widget('room_jid_entry') self._nickname_entry = self.xml.get_widget('nickname_entry') + self._password_entry = self.xml.get_widget('password_entry') self._room_jid_entry.set_text(room_jid) self._nickname_entry.set_text(nick) + if password: + self._password_entry.set_text(password) self.xml.signal_autoconnect(self) gajim.interface.instances[account]['join_gc'] = self #now add us to open windows if len(gajim.connections) > 1: @@ -1421,8 +1443,7 @@ class JoinGroupchatWindow: '''When Join button is clicked''' nickname = self._nickname_entry.get_text().decode('utf-8') room_jid = self._room_jid_entry.get_text().decode('utf-8') - password = self.xml.get_widget('password_entry').get_text().decode( - 'utf-8') + password = self._password_entry.get_text().decode('utf-8') user, server, resource = helpers.decompose_jid(room_jid) if not user or not server or resource: ErrorDialog(_('Invalid group chat Jabber ID'), @@ -1460,7 +1481,7 @@ class JoinGroupchatWindow: if not room_jid_bookmarked: name = gajim.get_nick_from_jid(room_jid) bmdict = { 'name': name, 'jid': room_jid, 'autojoin': u'1', - 'password': password, 'nick': nickname, + 'minimize': '0', 'password': password, 'nick': nickname, 'print_status': gajim.config.get('print_status_in_muc')} gajim.connections[self.account].bookmarks.append(bmdict) @@ -1858,17 +1879,6 @@ class SingleMessageWindow: spell2.set_language(lang) except gobject.GError, msg: dialogs.AspellDictError(lang) - self.send_button.set_no_show_all(True) - self.reply_button.set_no_show_all(True) - self.send_and_close_button.set_no_show_all(True) - self.to_label.set_no_show_all(True) - self.to_entry.set_no_show_all(True) - self.from_label.set_no_show_all(True) - self.from_entry.set_no_show_all(True) - self.close_button.set_no_show_all(True) - self.cancel_button.set_no_show_all(True) - self.message_scrolledwindow.set_no_show_all(True) - self.conversation_scrolledwindow.set_no_show_all(True) self.prepare_widgets_for(self.action) @@ -2217,7 +2227,7 @@ class PrivacyListWindow: jid_entry_completion.set_text_column(0) jid_entry_completion.set_model(jids_list_store) jid_entry_completion.set_popup_completion(True) - self.edit_type_jabberid_entry.set_completion(jid_entry_completion) + self.edit_type_jabberid_entry.set_completion(jid_entry_completion) if action == 'EDIT': self.refresh_rules() @@ -2231,7 +2241,6 @@ class PrivacyListWindow: self.window.set_title(title) - self.add_edit_vbox.set_no_show_all(True) self.window.show_all() self.add_edit_vbox.hide() @@ -2683,6 +2692,7 @@ class InvitationReceivedDialog: self.room_jid = room_jid self.account = account + self.password = password xml = gtkgui_helpers.get_glade('invitation_received_dialog.glade') self.dialog = xml.get_widget('invitation_received_dialog') @@ -2716,7 +2726,8 @@ class InvitationReceivedDialog: def on_accept_button_clicked(self, widget): self.dialog.destroy() try: - JoinGroupchatWindow(self.account, self.room_jid) + JoinGroupchatWindow(self.account, self.room_jid, + password=self.password) except GajimGeneralException: pass @@ -2796,7 +2807,10 @@ class ImageChooserDialog(FileChooserDialog): path_to_file = gtkgui_helpers.decode_filechooser_file_paths( (path_to_file,))[0] if os.path.exists(path_to_file): - callback(widget, path_to_file) + if isinstance(callback, tuple): + callback[0](widget, path_to_file, *callback[1:]) + else: + callback(widget, path_to_file) try: if os.name == 'nt': @@ -2856,12 +2870,19 @@ class AvatarChooserDialog(ImageChooserDialog): ImageChooserDialog.__init__(self, path_to_file, on_response_ok, on_response_cancel) button = gtk.Button(None, gtk.STOCK_CLEAR) + self.response_clear = on_response_clear if on_response_clear: - button.connect('clicked', on_response_clear) + button.connect('clicked', self.on_clear) button.show_all() self.action_area.pack_start(button) self.action_area.reorder_child(button, 0) + def on_clear(self, widget): + if isinstance(self.response_clear, tuple): + self.response_clear[0](widget, *self.response_clear[1:]) + else: + self.response_clear(widget) + class AddSpecialNotificationDialog: def __init__(self, jid): '''jid is the jid for which we want to add special notification @@ -2983,9 +3004,6 @@ class AdvancedNotificationsWindow: self.delete_button.set_sensitive(False) self.down_button.set_sensitive(False) self.up_button.set_sensitive(False) - self.recipient_list_entry.set_no_show_all(True) - for st in ['online', 'away', 'xa', 'dnd', 'invisible']: - self.__dict__[st + '_cb'].set_no_show_all(True) self.window.show_all() diff --git a/src/disco.py b/src/disco.py index 33b217efd..7446447e5 100644 --- a/src/disco.py +++ b/src/disco.py @@ -76,7 +76,7 @@ def _gen_agent_type_info(): ('_jid', 'weather'): (False, 'weather.png'), ('gateway', 'sip'): (False, 'sip.png'), ('directory', 'user'): (None, 'jud.png'), - ('pubsub', 'generic'): (None, 'pubsub.png'), + ('pubsub', 'generic'): (PubSubBrowser, 'pubsub.png'), ('pubsub', 'service'): (PubSubBrowser, 'pubsub.png'), ('proxy', 'bytestreams'): (None, 'bytestreams.png'), # Socks5 FT proxy @@ -438,8 +438,6 @@ _('Without a connection, you can not browse available services')) self.on_services_treeview_selection_changed) self.services_scrollwin = self.xml.get_widget('services_scrollwin') self.progressbar = self.xml.get_widget('services_progressbar') - self.progressbar.set_no_show_all(True) - self.progressbar.hide() self.banner = self.xml.get_widget('banner_agent_label') self.banner_icon = self.xml.get_widget('banner_agent_icon') self.banner_eventbox = self.xml.get_widget('banner_agent_eventbox') @@ -447,8 +445,6 @@ _('Without a connection, you can not browse available services')) self.banner.realize() self.paint_banner() self.filter_hbox = self.xml.get_widget('filter_hbox') - self.filter_hbox.set_no_show_all(True) - self.filter_hbox.hide() self.action_buttonbox = self.xml.get_widget('action_buttonbox') # Address combobox @@ -1047,7 +1043,7 @@ class ToplevelAgentBrowser(AgentBrowser): # as it was before setting the timeout if props and self.tooltip.id == props[0]: # bounding rectangle of coordinates for the cell within the treeview - rect = view.get_cell_area(props[0], props[1]) + rect = view.get_cell_area(props[0], props[1]) # position of the treeview on the screen position = view.window.get_origin() self.tooltip.show_tooltip(state, rect.height, position[1] + rect.y) @@ -1554,10 +1550,10 @@ class MucBrowser(AgentBrowser): self.join_button = None def _create_treemodel(self): - # JID, node, name, users, description, fetched + # JID, node, name, users_int, users_str, description, fetched # This is rather long, I'd rather not use a data_func here though. # Users is a string, because want to be able to leave it empty. - self.model = gtk.ListStore(str, str, str, str, str, bool) + self.model = gtk.ListStore(str, str, str, int, str, str, bool) self.model.set_sort_column_id(2, gtk.SORT_ASCENDING) self.window.services_treeview.set_model(self.model) # Name column @@ -1567,20 +1563,23 @@ class MucBrowser(AgentBrowser): renderer = gtk.CellRendererText() col.pack_start(renderer) col.set_attributes(renderer, text = 2) + col.set_sort_column_id(2) self.window.services_treeview.insert_column(col, -1) col.set_resizable(True) # Users column col = gtk.TreeViewColumn(_('Users')) renderer = gtk.CellRendererText() col.pack_start(renderer) - col.set_attributes(renderer, text = 3) + col.set_attributes(renderer, text = 4) + col.set_sort_column_id(3) self.window.services_treeview.insert_column(col, -1) col.set_resizable(True) # Description column col = gtk.TreeViewColumn(_('Description')) renderer = gtk.CellRendererText() col.pack_start(renderer) - col.set_attributes(renderer, text = 4) + col.set_attributes(renderer, text = 5) + col.set_sort_column_id(4) self.window.services_treeview.insert_column(col, -1) col.set_resizable(True) # Id column @@ -1588,9 +1587,11 @@ class MucBrowser(AgentBrowser): renderer = gtk.CellRendererText() col.pack_start(renderer) col.set_attributes(renderer, text = 0) + col.set_sort_column_id(0) self.window.services_treeview.insert_column(col, -1) col.set_resizable(True) self.window.services_treeview.set_headers_visible(True) + self.window.services_treeview.set_headers_clickable(True) # Source id for idle callback used to start disco#info queries. self._fetch_source = None # Query failure counter @@ -1692,7 +1693,7 @@ class MucBrowser(AgentBrowser): # We're at the end of the model, we can leave end=None though. pass while iter and self.model.get_path(iter) != end: - if not self.model.get_value(iter, 5): + if not self.model.get_value(iter, 6): jid = self.model.get_value(iter, 0).decode('utf-8') node = self.model.get_value(iter, 1).decode('utf-8') self.cache.get_info(jid, node, self._agent_info) @@ -1723,13 +1724,14 @@ class MucBrowser(AgentBrowser): if iter: if name: self.model[iter][2] = name - self.model[iter][3] = len(items) # The number of users - self.model[iter][5] = True + self.model[iter][3] = len(items) # The number of users + self.model[iter][4] = str(len(items)) # The number of users + self.model[iter][6] = True self._fetch_source = None self._query_visible() def _add_item(self, jid, node, item, force): - self.model.append((jid, node, item.get('name', ''), '', '', False)) + self.model.append((jid, node, item.get('name', ''), -1, '', '', False)) if not self._fetch_source: self._fetch_source = gobject.idle_add(self._start_info_query) @@ -1743,14 +1745,15 @@ class MucBrowser(AgentBrowser): users = form.getField('muc#roominfo_occupants') descr = form.getField('muc#roominfo_description') if users: - self.model[iter][3] = users.getValue() + self.model[iter][3] = int(users.getValue()) + self.model[iter][4] = users.getValue() if descr: - self.model[iter][4] = descr.getValue() + self.model[iter][5] = descr.getValue() # Only set these when we find a form with additional info # Some servers don't support forms and put extra info in # the name attribute, so we preserve it in that case. self.model[iter][2] = name - self.model[iter][5] = True + self.model[iter][6] = True break else: # We didn't find a form, switch to alternate query mode @@ -1791,8 +1794,9 @@ class DiscussionGroupsBrowser(AgentBrowser): def _create_treemodel(self): ''' Create treemodel for the window. ''' # JID, node, name (with description) - pango markup, dont have info?, subscribed? - self.model = gtk.ListStore(str, str, str, bool, bool) - self.model.set_sort_column_id(3, gtk.SORT_ASCENDING) + self.model = gtk.TreeStore(str, str, str, bool, bool) + # sort by name + self.model.set_sort_column_id(2, gtk.SORT_ASCENDING) self.window.services_treeview.set_model(self.model) # Name column @@ -1804,6 +1808,7 @@ class DiscussionGroupsBrowser(AgentBrowser): col.set_attributes(renderer, markup=2) col.set_resizable(True) self.window.services_treeview.insert_column(col, -1) + self.window.services_treeview.set_headers_visible(True) # Subscription state renderer = gtk.CellRendererToggle() @@ -1813,7 +1818,29 @@ class DiscussionGroupsBrowser(AgentBrowser): col.set_resizable(False) self.window.services_treeview.insert_column(col, -1) - self.window.services_treeview.set_headers_visible(True) + # Node Column + renderer = gtk.CellRendererText() + col = gtk.TreeViewColumn(_('Node')) + col.pack_start(renderer) + col.set_attributes(renderer, markup=1) + col.set_resizable(True) + self.window.services_treeview.insert_column(col, -1) + + def _add_items(self, jid, node, items, force): + for item in items: + jid = item['jid'] + node = item.get('node', '') + self._total_items += 1 + self._add_item(jid, node, item, force) + + def _in_list_foreach(self, model, path, iter, node): + if model[path][1] == node: + self.in_list = True + + def _in_list(self, node): + self.in_list = False + self.model.foreach(self._in_list_foreach, node) + return self.in_list def _add_item(self, jid, node, item, force): ''' Called when we got basic information about new node from query. @@ -1830,7 +1857,24 @@ class DiscussionGroupsBrowser(AgentBrowser): name = gobject.markup_escape_text(name) name = '%s' % name - self.model.append((jid, node, name, dunno, subscribed)) + node_splitted = node.split('/') + parent_iter = None + while len(node_splitted) > 1: + parent_node = node_splitted.pop(0) + parent_iter = self._get_child_iter(parent_iter, parent_node) + node_splitted[0] = parent_node + '/' + node_splitted[0] + if not self._in_list(node): + self.model.append(parent_iter, (jid, node, name, dunno, subscribed)) + self.cache.get_items(jid, node, self._add_items, force = force, + args = (force,)) + + def _get_child_iter(self, parent_iter, node): + child_iter = self.model.iter_children(parent_iter) + while child_iter: + if self.model[child_iter][1] == node: + return child_iter + child_iter = self.model.iter_next(child_iter) + return None def _add_actions(self): self.post_button = gtk.Button(label=_('New post'), use_underline=True) @@ -1905,7 +1949,7 @@ class DiscussionGroupsBrowser(AgentBrowser): model, iter = self.window.services_treeview.get_selection().get_selected() if iter is None: return - groupnode = model.get_value(iter, 1) # 1 = groupnode + groupnode = model.get_value(iter, 1) # 1 = groupnode gajim.connections[self.account].send_pb_unsubscribe(self.jid, groupnode, self._unsubscribeCB, groupnode) diff --git a/src/features_window.py b/src/features_window.py new file mode 100644 index 000000000..f0f7c061b --- /dev/null +++ b/src/features_window.py @@ -0,0 +1,276 @@ +## features_window.py +## +## Copyright (C) 2007 Yann Le Boulanger +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 2 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +import os +import gtk +import gobject +import gtkgui_helpers + +import dialogs + +from common import gajim +from common import helpers + +import random +from tempfile import gettempdir +from subprocess import Popen + +class FeaturesWindow: + '''Class for features window''' + + def __init__(self): + self.xml = gtkgui_helpers.get_glade('features_window.glade') + self.window = self.xml.get_widget('features_window') + treeview = self.xml.get_widget('features_treeview') + self.desc_label = self.xml.get_widget('feature_desc_label') + + # {name: (available_function, unix_text, windows_text)} + self.features = { + _('PyOpenSSL'): (self.pyopenssl_available, + _('A library used to validate server certificates to ensure a secure connection.'), + _('Requires python-pyopenssl.'), + _('Requires python-pyopenssl.')), + _('Bonjour / Zeroconf'): (self.zeroconf_available, + _('Serverless chatting with autodetected clients in a local network.'), + _('Requires python-avahi.'), + _('Requires pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour).')), + _('gajim-remote'): (self.dbus_available, + _('A script to controle gajim via commandline.'), + _('Requires python-dbus.'), + _('Feature not available under Windows.')), + _('OpenGPG'): (self.gpg_available, + _('Encrypting chatmessages with gpg keys.'), + _('Requires gpg and python-GnuPGInterface.'), + _('Feature not available under Windows.')), + _('network-manager'): (self.network_manager_available, + _('Autodetection of network status.'), + _('Requires gnome-network-manager and python-dbus.'), + _('Feature not available under Windows.')), + _('Session Management'): (self.session_management_available, + _('Gajim session is stored on logout and restored on login.'), + _('Requires python-gnome2.'), + _('Feature not available under Windows.')), + _('gnome-keyring'): (self.gnome_keyring_available, + _('Passwords can be stored securely and not just in plaintext.'), + _('Requires gnome-keyring and python-gnome2-desktop.'), + _('Feature not available under Windows.')), + _('SRV'): (self.srv_available, + _('Ability to connect to servers which is using SRV records.'), + _('Requires dnsutils.'), + _('Requires nslookup to use SRV records.')), + _('Spell Checker'): (self.speller_available, + _('Spellchecking of composed messages.'), + _('Requires python-gnome2-extras or compilation of gtkspell module from Gajim sources.'), + _('Feature not available under Windows.')), + _('Notification-daemon'): (self.notification_available, + _('Passive popups notifying for new events.'), + _('Requires python-notify or instead python-dbus in conjunction with notification-daemon.'), + _('Feature not available under Windows.')), + _('Trayicon'): (self.trayicon_available, + _('A icon in systemtray reflecting the current presence.'), + _('Requires python-gnome2-extras or compiled trayicon module from Gajim sources.'), + _('Requires PyGTK >= 2.10.')), + _('Idle'): (self.idle_available, + _('Ability to measure idle time, in order to set auto status.'), + _('Requires compilation of the idle module from Gajim sources.'), + _('Requires compilation of the idle module from Gajim sources.')), + _('LaTeX'): (self.latex_available, + _('Transform LaTeX espressions between $$ $$.'), + _('Requires texlive-latex-base, dvips and imagemagick. You have to set \'use_latex\' to True in the Advanced Configuration Editor.'), + _('Feature not available under Windows.')), + } + + # name, supported + self.model = gtk.ListStore(str, bool) + treeview.set_model(self.model) + + col = gtk.TreeViewColumn(_('Available')) + treeview.append_column(col) + cell = gtk.CellRendererToggle() + cell.set_property('radio', True) + col.pack_start(cell) + col.set_attributes(cell, active = 1) + + col = gtk.TreeViewColumn(_('Feature')) + treeview.append_column(col) + cell = gtk.CellRendererText() + col.pack_start(cell, expand = True) + col.add_attribute(cell, 'text', 0) + + # Fill model + for feature in self.features: + func = self.features[feature][0] + rep = func() + self.model.append([feature, rep]) + self.xml.signal_autoconnect(self) + self.window.show_all() + self.xml.get_widget('close_button').grab_focus() + + def on_close_button_clicked(self, widget): + self.window.destroy() + + def on_features_treeview_cursor_changed(self, widget): + selection = widget.get_selection() + path = selection.get_selected_rows()[1][0] + available = self.model[path][1] + feature = self.model[path][0] + text = self.features[feature][1] + '\n' + if os.name == 'nt': + text = text + self.features[feature][3] + else: + text = text + self.features[feature][2] + self.desc_label.set_text(text) + + def pyopenssl_available(self): + try: + import OpenSSL.SSL + import OpenSSL.crypto + except: + return False + return True + + def zeroconf_available(self): + try: + import avahi + except: + try: + import pybonjour + except: + return False + return True + + def dbus_available(self): + if os.name == 'nt': + return False + from common import dbus_support + return dbus_support.supported + + def gpg_available(self): + if os.name == 'nt': + return False + from common import GnuPG + return GnuPG.USE_GPG + + def network_manager_available(self): + if os.name == 'nt': + return False + import network_manager_listener + return network_manager_listener.supported + + def session_management_available(self): + if os.name == 'nt': + return False + try: + import gnome.ui + except: + return False + return True + + def gnome_keyring_available(self): + if os.name == 'nt': + return False + try: + import gnomekeyring + except: + return False + return True + + def srv_available(self): + return helpers.is_in_path('nslookup') + + def speller_available(self): + if os.name == 'nt': + return False + try: + import gtkspell + except: + return False + return True + + def notification_available(self): + if os.name == 'nt': + return False + from common import dbus_support + if self.dbus_available() and dbus_support.get_notifications_interface(): + return True + try: + import pynotify + except: + return False + return True + + def trayicon_available(self): + if os.name == 'nt' and gtk.pygtk_version >= (2, 10, 0) and \ + gtk.gtk_version >= (2, 10, 0): + return True + try: + import systray + except: + return False + return True + + def idle_available(self): + from common import sleepy + return sleepy.SUPPORTED + + def latex_available(self): + '''check is latex is available and if it can create a picture.''' + + if os.name == 'nt': + return False + + exitcode = 0 + random.seed() + tmpfile = os.path.join(gettempdir(), "gajimtex_" + \ + random.randint(0,100).__str__()) + + # build latex string + texstr = '\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}' + texstr += '\\usepackage{amsmath}\\usepackage{amssymb}\\pagestyle{empty}' + texstr += '\\begin{document}\\begin{large}\\begin{gather*}test' + texstr += '\\end{gather*}\\end{large}\\end{document}' + + file = open(os.path.join(tmpfile + ".tex"), "w+") + file.write(texstr) + file.flush() + file.close() + try: + p = Popen(['latex', '--interaction=nonstopmode', tmpfile + '.tex'], + cwd=gettempdir()) + exitcode = p.wait() + except: + exitcode = 1 + if exitcode == 0: + try: + p = Popen(['dvips', '-E', '-o', tmpfile + '.ps', tmpfile + '.dvi'], + cwd=gettempdir()) + exitcode = p.wait() + except: + exitcode = 1 + if exitcode == 0: + try: + p = Popen(['convert', tmpfile + '.ps', tmpfile + '.png'], + cwd=gettempdir()) + exitcode = p.wait() + except: + exitcode = 1 + extensions = [".tex", ".log", ".aux", ".dvi", ".ps", ".png"] + for ext in extensions: + try: + os.remove(tmpfile + ext) + except Exception: + pass + if exitcode == 0: + return True + return False diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py index ecc0c0479..8de1e4a74 100644 --- a/src/filetransfers_window.py +++ b/src/filetransfers_window.py @@ -116,8 +116,6 @@ class FileTransfersWindow: self.cancel_menuitem = self.xml.get_widget('cancel_menuitem') self.pause_menuitem = self.xml.get_widget('pause_menuitem') self.continue_menuitem = self.xml.get_widget('continue_menuitem') - self.continue_menuitem.hide() - self.continue_menuitem.set_no_show_all(True) self.remove_menuitem = self.xml.get_widget('remove_menuitem') self.xml.signal_autoconnect(self) @@ -420,10 +418,18 @@ _('Connection with peer cannot be established.')) #they are not translatable. return _('%(hours)02.d:%(minutes)02.d:%(seconds)02.d') % times - def _get_eta_and_speed(self, full_size, transfered_size, elapsed_time): - if elapsed_time == 0: + def _get_eta_and_speed(self, full_size, transfered_size, file_props): + if len(file_props['transfered_size']) == 0: return 0., 0. - speed = round(float(transfered_size) / elapsed_time) + elif len(file_props['transfered_size']) == 1: + speed = round(float(transfered_size) / file_props['elapsed-time']) + else: + # first and last are (time, transfered_size) + first = file_props['transfered_size'][0] + last = file_props['transfered_size'][-1] + transfered = last[1] - first[1] + tim = last[0] - first[0] + speed = round(float(transfered) / tim) if speed == 0.: return 0., 0. remaining_size = full_size - transfered_size @@ -484,8 +490,13 @@ _('Connection with peer cannot be established.')) if file_props.has_key('offset') and file_props['offset']: transfered_size -= file_props['offset'] full_size -= file_props['offset'] + + if file_props['elapsed-time'] > 0: + file_props['transfered_size'].append((file_props['last-time'], transfered_size)) + if len(file_props['transfered_size']) > 6: + file_props['transfered_size'].pop(0) eta, speed = self._get_eta_and_speed(full_size, transfered_size, - file_props['elapsed-time']) + file_props) self.model.set(iter, C_PROGRESS, text) self.model.set(iter, C_PERCENT, int(percent)) @@ -547,6 +558,8 @@ _('Connection with peer cannot be established.')) file_props['sender'] = account file_props['receiver'] = contact file_props['tt_account'] = account + # keep the last time: transfered_size to compute transfer speed + file_props['transfered_size'] = [] return file_props def add_transfer(self, account, contact, file_props): @@ -784,6 +797,8 @@ _('Connection with peer cannot be established.')) elif self.is_transfer_active(file_props): file_props['paused'] = True self.set_status(file_props['type'], file_props['sid'], 'pause') + # reset that to compute speed only when we resume + file_props['transfered_size'] = [] self.toggle_pause_continue(False) def on_cancel_button_clicked(self, widget): diff --git a/src/gajim-remote.py b/src/gajim-remote.py index e350409e5..91ff086a6 100755 --- a/src/gajim-remote.py +++ b/src/gajim-remote.py @@ -137,6 +137,15 @@ class GajimRemote: 'using this account'), False), ] ], + 'send_groupchat_message':[ + _('Sends new message to a groupchat you\'ve joined.'), + [ + ('room_jid', _('JID of the room that will receive the message'), True), + (_('message'), _('message contents'), True), + (_('account'), _('if specified, the message will be sent ' + 'using this account'), False), + ] + ], 'contact_info': [ _('Gets detailed info on a contact'), [ diff --git a/src/gajim.py b/src/gajim.py index 37970697d..b9e12222c 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -6,6 +6,8 @@ ## Copyright (C) 2005-2006 Nikos Kouremenos ## Copyright (C) 2005-2006 Dimitur Kirov ## Copyright (C) 2005 Travis Shirk +## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -70,10 +72,11 @@ def parseAndSetLogLevels(arg): def parseOpts(): profile = '' verbose = False + config_path = None try: - shortargs = 'hqvl:p:' - longargs = 'help quiet verbose loglevel= profile=' + shortargs = 'hqvl:p:c:' + longargs = 'help quiet verbose loglevel= profile= config_path=' opts, args = getopt.getopt(sys.argv[1:], shortargs, longargs.split()) except getopt.error, msg: print msg @@ -93,11 +96,22 @@ def parseOpts(): profile = a elif o in ('-l', '--loglevel'): parseAndSetLogLevels(a) - return profile, verbose + elif o in ('-c', '--config-path'): + config_path = a + return profile, verbose, config_path -profile, verbose = parseOpts() +profile, verbose, config_path = parseOpts() del parseOpts, parseAndSetLogLevels, parseLogTarget, parseLogLevel +import locale +profile = unicode(profile, locale.getpreferredencoding()) + +import common.configpaths +common.configpaths.gajimpaths.init(config_path) +del config_path +common.configpaths.gajimpaths.init_profile(profile) +del profile + import message_control from chat_control import ChatControlBase @@ -106,6 +120,8 @@ from atom_window import AtomWindow from common import exceptions from common.zeroconf import connection_zeroconf from common import dbus_support +if dbus_support.supported: + import dbus if os.name == 'posix': # dl module is Unix Only try: # rename the process name to gajim @@ -196,12 +212,6 @@ from common import optparser if verbose: gajim.verbose = True del verbose -import locale -profile = unicode(profile, locale.getpreferredencoding()) - -import common.configpaths -common.configpaths.init_profile(profile) -del profile gajimpaths = common.configpaths.gajimpaths pid_filename = gajimpaths['PID_FILE'] @@ -316,9 +326,15 @@ pid_dir = os.path.dirname(pid_filename) if not os.path.exists(pid_dir): check_paths.create_path(pid_dir) # Create pid file -f = open(pid_filename, 'w') -f.write(str(os.getpid())) -f.close() +try: + f = open(pid_filename, 'w') + f.write(str(os.getpid())) + f.close() +except IOError, e: + dlg = dialogs.ErrorDialog(_('Disk Write Error'), str(e)) + dlg.run() + dlg.destroy() + sys.exit() del pid_dir del f @@ -396,6 +412,9 @@ class Interface: prompt = data[2] proposed_nick = data[3] gc_control = self.msg_win_mgr.get_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 gc_control: # user may close the window before we are here gc_control.show_change_nick_input_dialog(title, prompt, proposed_nick) @@ -479,16 +498,21 @@ class Interface: model[self.roster.status_message_menuitem_iter][3] = True # Inform all controls for this account of the connection state change - for ctrl in self.msg_win_mgr.get_controls(): + ctrls = self.msg_win_mgr.get_controls() + if self.minimized_controls.has_key(account): + # Can not be the case when we remove account + ctrls += self.minimized_controls[account].values() + for ctrl in ctrls: if ctrl.account == account: if status == 'offline' or (status == 'invisible' and \ - gajim.connections[account].is_zeroconf): + 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() - ctrl.parent_win.redraw_tab(ctrl) + if ctrl.parent_win: + ctrl.parent_win.redraw_tab(ctrl) self.roster.on_status_changed(account, status) if account in self.show_vcard_when_connect: @@ -535,7 +559,7 @@ class Interface: # 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_from_jid(account, ji) + lcontact = gajim.contacts.get_contacts(account, ji) contact1 = None resources = [] for c in lcontact: @@ -636,7 +660,7 @@ class Interface: # (when contact signs out or has errors) if array[1] in ('offline', 'error'): contact1.our_chatstate = contact1.chatstate = \ - contact1.composing_jep = None + contact1.composing_xep = None gajim.connections[account].remove_transfers_for_contact(contact1) self.roster.chg_contact_status(contact1, array[1], status_message, account) @@ -665,7 +689,7 @@ class Interface: def handle_event_msg(self, account, array): # 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject, - # chatstate, msg_id, composing_jep, user_nick, xhtml)) + # chatstate, msg_id, composing_xep, user_nick, xhtml)) # user_nick is JEP-0172 full_jid_with_resource = array[0] @@ -678,7 +702,7 @@ class Interface: subject = array[5] chatstate = array[6] msg_id = array[7] - composing_jep = array[8] + composing_xep = array[8] xhtml = array[10] if gajim.config.get('ignore_incoming_xhtml'): xhtml = None @@ -687,9 +711,8 @@ class Interface: groupchat_control = self.msg_win_mgr.get_control(jid, account) if not groupchat_control and \ - gajim.interface.minimized_controls.has_key(account) and \ - jid in gajim.interface.minimized_controls[account]: - groupchat_control = gajim.interface.minimized_controls[account][jid] + jid in self.minimized_controls[account]: + groupchat_control = self.minimized_controls[account][jid] pm = False if groupchat_control and groupchat_control.type_id == \ message_control.TYPE_GC: @@ -720,11 +743,9 @@ class Interface: # Handle chat states contact = gajim.contacts.get_contact(account, jid, resource) - if contact and isinstance(contact, list): - contact = contact[0] if contact: - if contact.composing_jep != 'JEP-0085': # We cache xep85 support - contact.composing_jep = composing_jep + if contact.composing_xep != 'XEP-0085': # We cache xep85 support + contact.composing_xep = composing_xep if chat_control and chat_control.type_id == message_control.TYPE_CHAT: if chatstate is not None: # other peer sent us reply, so he supports jep85 or jep22 @@ -748,7 +769,7 @@ class Interface: return if gajim.config.get('ignore_unknown_contacts') and \ - not gajim.contacts.get_contact(account, jid) and not pm: + not gajim.contacts.get_contacts(account, jid) and not pm: return if not contact: # contact is not in the roster, create a fake one to display @@ -769,7 +790,7 @@ class Interface: if pm: nickname = resource groupchat_control.on_private_message(nickname, message, array[2], - xhtml) + xhtml, msg_id) else: # array: (jid, msg, time, encrypted, msg_type, subject) if encrypted: @@ -798,6 +819,9 @@ class Interface: jids = full_jid_with_resource.split('/', 1) jid = jids[0] gc_control = self.msg_win_mgr.get_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: @@ -821,7 +845,7 @@ class Interface: return gc_control.print_conversation('Error %s: %s' % (array[1], array[2])) - if gc_control.parent_win.get_active_jid() == jid: + if gc_control.parent_win and gc_control.parent_win.get_active_jid() == jid: gc_control.set_subject(gc_control.subject) return @@ -991,6 +1015,9 @@ class Interface: win.set_values(array) if account in self.show_vcard_when_connect: self.show_vcard_when_connect.remove(account) + jid = array['jid'] + if self.instances[account]['infos'].has_key(jid): + self.instances[account]['infos'][jid].set_values(array) def handle_event_vcard(self, account, vcard): # ('VCARD', account, data) @@ -1026,6 +1053,9 @@ class Interface: # Show avatar in roster or gc_roster gc_ctrl = self.msg_win_mgr.get_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: @@ -1042,10 +1072,6 @@ class Interface: win = self.instances[account]['infos'][array[0] + '/' + array[1]] if win: c = gajim.contacts.get_contact(account, array[0], array[1]) - # c is a list when no resource is given. it probably means that contact - # is offline, so only on Contact instance - if isinstance(c, list) and len(c): - c = c[0] if c: # c can be none if it's a gc contact c.last_status_time = time.localtime(time.time() - array[2]) if array[3]: @@ -1081,7 +1107,6 @@ class Interface: # PrivateChatControl control = self.msg_win_mgr.get_control(room_jid, account) if not control and \ - self.minimized_controls.has_key(account) and \ room_jid in self.minimized_controls[account]: control = self.minimized_controls[account][room_jid] @@ -1090,8 +1115,11 @@ class Interface: if control: control.chg_contact_status(nick, show, status, array[4], array[5], array[6], array[7], array[8], array[9], array[10], array[11]) - if control and not control.parent_win: - gajim.interface.roster.draw_contact(room_jid, account) + + contact = gajim.contacts.\ + get_contact_with_highest_priority(account, room_jid) + if contact: + self.roster.draw_contact(room_jid, account) ctrl = self.msg_win_mgr.get_control(fjid, account) @@ -1119,7 +1147,6 @@ class Interface: gc_control = self.msg_win_mgr.get_control(room_jid, account) if not gc_control and \ - self.minimized_controls.has_key(account) and \ room_jid in self.minimized_controls[account]: gc_control = self.minimized_controls[account][room_jid] @@ -1141,7 +1168,7 @@ class Interface: contact = gajim.contacts.\ get_contact_with_highest_priority(account, room_jid) if contact: - gajim.interface.roster.draw_contact(room_jid, account) + self.roster.draw_contact(room_jid, account) if self.remote_ctrl: self.remote_ctrl.raise_signal('GCMessage', (account, array)) @@ -1154,7 +1181,6 @@ class Interface: gc_control = self.msg_win_mgr.get_control(jid, account) if not gc_control and \ - self.minimized_controls.has_key(account) and \ jid in self.minimized_controls[account]: gc_control = self.minimized_controls[account][jid] @@ -1162,7 +1188,7 @@ class Interface: get_contact_with_highest_priority(account, jid) if contact: contact.status = array[1] - gajim.interface.roster.draw_contact(jid, account) + self.roster.draw_contact(jid, account) if not gc_control: return @@ -1197,6 +1223,46 @@ class Interface: 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_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_contol.py) + changes.append(_('Any occupant is allowed to see your full JID')) + 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_contol.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')) + if '173' in statusCode: + changes.append(_('Room is now semi-anonymous')) + if '174' in statusCode: + changes.append(_('Room is now fully-anonymous')) + + for change in changes: + gc_control.print_conversation(change) + def handle_event_gc_affiliation(self, account, array): #('GC_AFFILIATION', account, (room_jid, affiliation, list)) list is list room_jid = array[0] @@ -1204,6 +1270,29 @@ class Interface: self.instances[account]['gc_config'][room_jid].\ affiliation_list_received(array[1], array[2]) + 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 = win.get_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)) jid = gajim.get_jid_without_resource(array[1]) @@ -1240,7 +1329,7 @@ class Interface: sub = array[2] ask = array[3] groups = array[4] - contacts = gajim.contacts.get_contacts_from_jid(account, jid) + contacts = gajim.contacts.get_contacts(account, jid) # contact removes us. if (not sub or sub == 'none') and (not ask or ask == 'none') and \ not name and not groups: @@ -1304,11 +1393,11 @@ class Interface: # join autojoinable rooms for bm in bms: + minimize = bm['minimize'] in ('1', 'true') if bm['autojoin'] in ('1', 'true'): self.roster.join_gc_room(account, bm['jid'], bm['nick'], - bm['password'], - minimize = gajim.config.get('minimize_autojoined_rooms')) - + bm['password'], minimize = minimize) + def handle_event_file_send_error(self, account, array): jid = array[0] file_props = array[1] @@ -1620,7 +1709,8 @@ class Interface: if self.instances[account].has_key('profile'): win = self.instances[account]['profile'] win.vcard_published() - for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC): + 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 @@ -1648,7 +1738,8 @@ class Interface: if gajim.connections[account].connected == invisible_show: return # join already open groupchats - for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC): + 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 @@ -2080,8 +2171,10 @@ class Interface: '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, @@ -2138,6 +2231,12 @@ class Interface: 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 self.minimized_controls[account].has_key(jid): + if not w: + ctrl = self.minimized_controls[account][jid] + w = self.msg_win_mgr.create_window(ctrl.contact, \ + ctrl.account, ctrl.type_id) + self.roster.on_groupchat_maximized(None, jid, account) elif type_ in ('printed_chat', 'chat', ''): # '' is for log in/out notifications if self.msg_win_mgr.has_window(fjid, account): @@ -2153,8 +2252,10 @@ class Interface: gajim.events.change_jid(account, fjid, jid) resource = None fjid = jid - contact = gajim.contacts.get_contact(account, jid, resource) - if not contact or isinstance(contact, list): + contact = None + if resource: + contact = gajim.contacts.get_contact(account, jid, resource) + if not contact: contact = highest_contact self.roster.new_chat(contact, account, resource = resource) w = self.msg_win_mgr.get_window(fjid, account) @@ -2173,8 +2274,7 @@ class Interface: show = 'offline' gc_contact = gajim.contacts.create_gc_contact( room_jid = room_jid, name = nick, show = show) - c = gajim.contacts.contact_from_gc_contact(gc_contact) - self.roster.new_chat(c, account, private_chat = True) + self.roster.new_private_chat(gc_contact, account) w = self.msg_win_mgr.get_window(fjid, account) elif type_ in ('normal', 'file-request', 'file-request-error', 'file-send-error', 'file-error', 'file-stopped', 'file-completed'): @@ -2183,15 +2283,17 @@ class Interface: 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 = 'http://mail.google.com/mail?account_id=%s' % urllib.quote( - gajim.config.get_per('accounts', account, 'name')) - helpers.launch_browser_mailer('url', url) + 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 @@ -2217,6 +2319,8 @@ class Interface: # 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.default_colors = { 'inmsgcolor': gajim.config.get('inmsgcolor'), 'outmsgcolor': gajim.config.get('outmsgcolor'), @@ -2252,7 +2356,7 @@ class Interface: #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', 'gtk+') + gajim.config.set('roster_theme', _('default')) if len(gajim.config.get_per('themes')) == 0: d = ['accounttextcolor', 'accountbgcolor', 'accountfont', 'accountfontattrs', 'grouptextcolor', 'groupbgcolor', 'groupfont', @@ -2309,6 +2413,7 @@ class Interface: for a in gajim.connections: self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {}} + self.minimized_controls[a] = {} gajim.contacts.add_account(a) gajim.groups[a] = {} gajim.gc_connected[a] = {} @@ -2335,11 +2440,35 @@ class Interface: self.remote_ctrl = None if gajim.config.get('networkmanager_support') and dbus_support.supported: - try: - import network_manager_listener - except: + import network_manager_listener + if not network_manager_listener.supported: print >> sys.stderr, _('Network Manager support not available') + # Handle gnome screensaver + if dbus_support.supported: + def gnome_screensaver_ActiveChanged_cb(active): + if not active: + return + for account in gajim.connections: + if not gajim.sleeper_state.has_key(account) 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 + self.roster.send_status(account, 'away', auto_message, + auto=True) + gajim.sleeper_state[account] = 'autoaway' + + bus = dbus.SessionBus() + bus.add_signal_receiver(gnome_screensaver_ActiveChanged_cb, + 'ActiveChanged', 'org.gnome.ScreenSaver') + self.show_vcard_when_connect = [] path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png') @@ -2393,8 +2522,11 @@ class Interface: self.last_ftwindow_update = 0 gobject.timeout_add(100, self.autoconnect) - gobject.timeout_add(200, self.process_connections) - gobject.timeout_add(500, self.read_sleepy) + if os.name == 'nt': + gobject.timeout_add(200, self.process_connections) + else: + gobject.timeout_add(2000, self.process_connections) + gobject.timeout_add(10000, self.read_sleepy) if __name__ == '__main__': def sigint_cb(num, stack): diff --git a/src/gajim_themes_window.py b/src/gajim_themes_window.py index 6b0200ecb..3454ee79f 100644 --- a/src/gajim_themes_window.py +++ b/src/gajim_themes_window.py @@ -50,6 +50,7 @@ class GajimThemesWindow: self.italic_togglebutton = self.xml.get_widget('italic_togglebutton') self.themes_tree = self.xml.get_widget('themes_treeview') self.theme_options_vbox = self.xml.get_widget('theme_options_vbox') + self.theme_options_table = self.xml.get_widget('theme_options_table') self.colorbuttons = {} for chatstate in ('inactive', 'composing', 'paused', 'gone', 'muc_msg', 'muc_directed_msg'): @@ -67,6 +68,7 @@ class GajimThemesWindow: self.current_theme = gajim.config.get('roster_theme') self.no_update = False self.fill_themes_treeview() + self.select_active_theme() self.current_option = self.options[0] self.set_theme_options(self.current_theme, self.current_option) @@ -81,6 +83,8 @@ class GajimThemesWindow: return True # do NOT destroy the window def on_close_button_clicked(self, widget): + if gajim.interface.instances.has_key('preferences'): + gajim.interface.instances['preferences'].update_theme_list() self.window.hide() def on_theme_cell_edited(self, cell, row, new_name): @@ -90,6 +94,11 @@ class GajimThemesWindow: new_name = new_name.decode('utf-8') if old_name == new_name: return + if old_name == 'default': + dialogs.ErrorDialog( + _('You cannot make changes to the default theme'), + _('Please create a clean new theme with your desired name.')) + return new_config_name = new_name.replace(' ', '_') if new_config_name in gajim.config.get_per('themes'): return @@ -110,28 +119,31 @@ class GajimThemesWindow: self.current_theme = new_name def fill_themes_treeview(self): - self.xml.get_widget('remove_button').set_sensitive(False) - self.theme_options_vbox.set_sensitive(False) model = self.themes_tree.get_model() model.clear() for config_theme in gajim.config.get_per('themes'): theme = config_theme.replace('_', ' ') iter = model.append([theme]) - if gajim.config.get('roster_theme') == config_theme: - self.themes_tree.get_selection().select_iter(iter) - self.xml.get_widget('remove_button').set_sensitive(True) - self.theme_options_vbox.set_sensitive(True) def select_active_theme(self): model = self.themes_tree.get_model() iter = model.get_iter_root() - active_theme = gajim.config.get('roster_theme') + active_theme = gajim.config.get('roster_theme').replace('_', ' ') while iter: theme = model[iter][0] if theme == active_theme: self.themes_tree.get_selection().select_iter(iter) self.xml.get_widget('remove_button').set_sensitive(True) self.theme_options_vbox.set_sensitive(True) + self.theme_options_table.set_sensitive(True) + if active_theme == 'default': + self.xml.get_widget('remove_button').set_sensitive(False) + self.theme_options_vbox.set_sensitive(False) + self.theme_options_table.set_sensitive(False) + else: + self.xml.get_widget('remove_button').set_sensitive(True) + self.theme_options_vbox.set_sensitive(True) + self.theme_options_table.set_sensitive(True) break iter = model.iter_next(iter) @@ -140,12 +152,19 @@ class GajimThemesWindow: selected = self.themes_tree.get_selection().get_selected_rows() if not iter or selected[1] == []: self.theme_options_vbox.set_sensitive(False) + self.theme_options_table.set_sensitive(False) return - self.xml.get_widget('remove_button').set_sensitive(True) - self.theme_options_vbox.set_sensitive(True) self.current_theme = model.get_value(iter, 0).decode('utf-8') self.current_theme = self.current_theme.replace(' ', '_') self.set_theme_options(self.current_theme) + if self.current_theme == 'default': + self.xml.get_widget('remove_button').set_sensitive(False) + self.theme_options_vbox.set_sensitive(False) + self.theme_options_table.set_sensitive(False) + else: + self.xml.get_widget('remove_button').set_sensitive(True) + self.theme_options_vbox.set_sensitive(True) + self.theme_options_table.set_sensitive(True) def on_add_button_clicked(self, widget): model = self.themes_tree.get_model() @@ -173,6 +192,8 @@ class GajimThemesWindow: _('Please first choose another for your current theme.')) return self.theme_options_vbox.set_sensitive(False) + self.theme_options_table.set_sensitive(False) + self.xml.get_widget('remove_button').set_sensitive(False) gajim.config.del_per('themes', self.current_theme) model.remove(iter) diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 5757b865f..1315f1442 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -9,6 +9,8 @@ ## Norman Rasmussen ## Copyright (C) 2006 Travis Shirk ## Copyright (C) 2005-2007 Nikos Kouremenos +## Copyright (C) 2007 Julien Pivotto +## Copyright (C) 2007 Lukas Petrovicky ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -63,6 +65,12 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None): # cell data func is global, because we don't want it to keep # reference to GroupchatControl instance (self) theme = gajim.config.get('roster_theme') + # allocate space for avatar only if needed + if isinstance(renderer, gtk.CellRendererPixbuf): + if model[iter][C_AVATAR]: + renderer.set_property('visible', True) + else: + renderer.set_property('visible', False) if model.iter_parent(iter): bgcolor = gajim.config.get_per('themes', theme, 'contactbgcolor') if bgcolor: @@ -97,12 +105,14 @@ def tree_cell_data_func(column, renderer, model, iter, tv=None): class PrivateChatControl(ChatControl): TYPE_ID = message_control.TYPE_PM - def __init__(self, parent_win, gc_contact, contact, acct): + def __init__(self, parent_win, gc_contact, contact, account): room_jid = contact.jid.split('/')[0] - room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, acct) + room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, account) + if gajim.interface.minimized_controls[account].has_key(room_jid): + room_ctrl = gajim.interface.minimized_controls[account][room_jid] self.room_name = room_ctrl.name self.gc_contact = gc_contact - ChatControl.__init__(self, parent_win, contact, acct) + ChatControl.__init__(self, parent_win, contact, account) self.TYPE_ID = 'pm' def send_message(self, message): @@ -127,7 +137,7 @@ class PrivateChatControl(ChatControl): return ChatControl.send_message(self, message) - + def update_ui(self): if self.contact.show == 'offline': self.got_disconnected() @@ -183,9 +193,8 @@ class GroupchatControl(ChatControlBase): self.nick = contact.name self.name = self.room_jid.split('@')[0] - hide_chat_buttons_always = gajim.config.get( - 'always_hide_groupchat_buttons') - self.chat_buttons_set_visible(hide_chat_buttons_always) + compact_view = gajim.config.get('compact_view') + self.chat_buttons_set_visible(compact_view) self.widget_set_visible(self.xml.get_widget('banner_eventbox'), gajim.config.get('hide_groupchat_banner')) self.widget_set_visible(self.xml.get_widget('list_scrolledwindow'), @@ -237,18 +246,14 @@ class GroupchatControl(ChatControlBase): self._on_change_subject_menuitem_activate) self.handlers[id] = self.change_subject_menuitem - self.compact_view_menuitem = xm.get_widget('compact_view_menuitem') - id = self.compact_view_menuitem.connect('activate', - self._on_compact_view_menuitem_activate) - self.handlers[id] = self.compact_view_menuitem - widget = xm.get_widget('history_menuitem') id = widget.connect('activate', self._on_history_menuitem_activate) self.handlers[id] = widget - widget = xm.get_widget('minimize_menuitem') - id = widget.connect('activate', self._on_minimize_menuitem_activate) - self.handlers[id] = widget + self.minimize_menuitem = xm.get_widget('minimize_menuitem') + id = self.minimize_menuitem.connect('toggled', + self.on_minimize_menuitem_toggled) + self.handlers[id] = self.minimize_menuitem self.gc_popup_menu = xm.get_widget('gc_control_popup_menu') @@ -285,14 +290,8 @@ class GroupchatControl(ChatControlBase): # first one img, second one text, third is sec pixbuf column = gtk.TreeViewColumn() - renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image - column.pack_start(renderer_pixbuf, expand = False) - column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR) - column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func, - self.list_treeview) - renderer_pixbuf.set_property('xalign', 1) # align pixbuf to the right - renderer_image = cell_renderer_image.CellRendererImage(0, 0) # status img + renderer_image.set_property('width', 26) column.pack_start(renderer_image, expand = False) column.add_attribute(renderer_image, 'image', C_IMG) column.set_cell_data_func(renderer_image, tree_cell_data_func, @@ -301,9 +300,17 @@ class GroupchatControl(ChatControlBase): renderer_text = gtk.CellRendererText() # nickname column.pack_start(renderer_text, expand = True) column.add_attribute(renderer_text, 'markup', C_TEXT) + renderer_text.set_property("ellipsize", pango.ELLIPSIZE_END) column.set_cell_data_func(renderer_text, tree_cell_data_func, self.list_treeview) + renderer_pixbuf = gtk.CellRendererPixbuf() # avatar image + column.pack_start(renderer_pixbuf, expand = False) + column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR) + column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func, + self.list_treeview) + renderer_pixbuf.set_property('xalign', 1) # align pixbuf to the right + self.list_treeview.append_column(column) # workaround to avoid gtk arrows to be shown @@ -483,10 +490,10 @@ class GroupchatControl(ChatControlBase): self.name_label.set_markup(text) def prepare_context_menu(self): - '''sets compact view menuitem active state - sets sensitivity state for configure_room''' - # Check compact view menuitem - self.compact_view_menuitem.set_active(self.hide_chat_buttons_current) + '''sets sensitivity state for configure_room''' + if self.contact.jid in gajim.config.get_per('accounts', self.account, + 'minimized_gc').split(' '): + self.minimize_menuitem.set_active(True) if gajim.gc_connected[self.account][self.room_jid]: c = gajim.contacts.get_gc_contact(self.account, self.room_jid, self.nick) @@ -515,11 +522,13 @@ class GroupchatControl(ChatControlBase): else: # message from someone if has_timestamp: - self.print_old_conversation(msg, nick, tim, xhtml) + # don't print xhtml if it's an old message. + # Like that xhtml messages are grayed too. + self.print_old_conversation(msg, nick, tim, None) else: self.print_conversation(msg, nick, tim, xhtml) - def on_private_message(self, nick, msg, tim, xhtml): + def on_private_message(self, nick, msg, tim, xhtml, msg_id = None): # Do we have a queue? fjid = self.room_jid + '/' + nick no_queue = len(gajim.events.get_events(self.account, fjid)) == 0 @@ -531,7 +540,7 @@ class GroupchatControl(ChatControlBase): return event = gajim.events.create_event('pm', (msg, '', 'incoming', tim, - False, '', None, xhtml)) + False, '', msg_id, xhtml)) gajim.events.add_event(self.account, fjid, event) autopopup = gajim.config.get('autopopup') @@ -544,8 +553,8 @@ class GroupchatControl(ChatControlBase): model = self.list_treeview.get_model() state_images =\ gajim.interface.roster.get_appropriate_state_images( - self.room_jid, icon_name = 'message') - image = state_images['message'] + self.room_jid, icon_name = 'event') + image = state_images['event'] model[iter][C_IMG] = image if self.parent_win: self.parent_win.show_title() @@ -556,7 +565,10 @@ class GroupchatControl(ChatControlBase): self.list_treeview.expand_row(path[0:1], False) self.list_treeview.scroll_to_cell(path) self.list_treeview.set_cursor(path) - gajim.interface.roster.draw_contact(self.room_jid, self.account) + contact = gajim.contacts.get_contact_with_highest_priority(self.account, \ + self.room_jid) + if contact: + gajim.interface.roster.draw_contact(self.room_jid, self.account) def get_contact_iter(self, nick): model = self.list_treeview.get_model() @@ -662,8 +674,7 @@ class GroupchatControl(ChatControlBase): other_tags_for_text.append('gc_nickname_color_' + \ str(self.gc_custom_colors[contact])) - if self.parent_win: - self.check_and_possibly_add_focus_out_line() + self.check_and_possibly_add_focus_out_line() ChatControlBase.print_conversation_line(self, text, kind, contact, tim, other_tags_for_name, [], other_tags_for_text, xhtml = xhtml) @@ -697,7 +708,7 @@ class GroupchatControl(ChatControlBase): if self.needs_visual_notification(text): highlight = True if gajim.config.get_per('soundevents', 'muc_message_highlight', - 'enabled'): + 'enabled'): sound = 'highlight' # Is it a history message? Don't want sound-floods when we join. @@ -712,7 +723,7 @@ class GroupchatControl(ChatControlBase): it removes previous line first''' win = gajim.interface.msg_win_mgr.get_window(self.room_jid, self.account) - if self.room_jid == win.get_active_jid() and\ + if win and self.room_jid == win.get_active_jid() and\ win.window.get_property('has-toplevel-focus') and\ self.parent_win.get_active_control() == self: # it's the current room and it's the focused window. @@ -780,7 +791,8 @@ class GroupchatControl(ChatControlBase): gc_contact.show = 'offline' gc_contact.status = '' ctrl.update_ui() - ctrl.parent_win.redraw_tab(ctrl) + if ctrl.parent_win: + ctrl.parent_win.redraw_tab(ctrl) gajim.contacts.remove_gc_contact(self.account, gc_contact) gajim.gc_connected[self.account][self.room_jid] = False ChatControlBase.got_disconnected(self) @@ -795,6 +807,8 @@ class GroupchatControl(ChatControlBase): self.add_contact_to_roster(nick, gc_contact.show, gc_contact.role, gc_contact.affiliation, gc_contact.status, gc_contact.jid) + # Recalculate column width for ellipsizin + self.list_treeview.columns_autosize() def on_send_pm(self, widget = None, model = None, iter = None, nick = None, msg = None): @@ -809,6 +823,11 @@ class GroupchatControl(ChatControlBase): gajim.interface.msg_win_mgr.get_control(fjid, self.account).\ send_message(msg) + def on_send_file(self, widget, gc_contact): + '''sends a file to a contact in the room''' + gajim.interface.instances['file_transfers'].show_file_send_request( + self.account, gc_contact) + def draw_contact(self, nick, selected=False, focus=False): iter = self.get_contact_iter(nick) if not iter: @@ -818,7 +837,7 @@ class GroupchatControl(ChatControlBase): nick) state_images = gajim.interface.roster.jabber_state_images['16'] if len(gajim.events.get_events(self.account, self.room_jid + '/' + nick)): - image = state_images['message'] + image = state_images['event'] else: image = state_images[gc_contact.show] @@ -867,66 +886,99 @@ class GroupchatControl(ChatControlBase): affiliation = 'none' fake_jid = self.room_jid + '/' + nick newly_created = False + + # statusCode + # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-init + if statusCode: + if '100' in statusCode: + # Can be a message (see handle_event_gc_config_change in gajim.py) + self.print_conversation(\ + _('Any occupant is allowed to see your full JID')) + if '170' in statusCode: + # Can be a message (see handle_event_gc_config_change in gajim.py) + self.print_conversation(_('Room logging is enabled')) + if '201' in statusCode: + self.print_conversation(_('A new room has been created')) + if '210' in statusCode: + self.print_conversation(\ + _('The server has assigned or modified your roomnick')) + if show in ('offline', 'error'): - if statusCode == '307': - if actor is None: # do not print 'kicked by None' - s = _('%(nick)s has been kicked: %(reason)s') % { + if statusCode: + if '307' in statusCode: + if actor is None: # do not print 'kicked by None' + s = _('%(nick)s has been kicked: %(reason)s') % { + 'nick': nick, + 'reason': reason } + else: + s = _('%(nick)s has been kicked by %(who)s: %(reason)s') % { + 'nick': nick, + 'who': actor, + 'reason': reason } + self.print_conversation(s, 'info', tim = tim) + elif '301' in statusCode: + if actor is None: # do not print 'banned by None' + s = _('%(nick)s has been banned: %(reason)s') % { + 'nick': nick, + 'reason': reason } + else: + s = _('%(nick)s has been banned by %(who)s: %(reason)s') % { + 'nick': nick, + 'who': actor, + 'reason': reason } + self.print_conversation(s, 'info', tim = tim) + elif '303' in statusCode: # Someone changed his or her nick + if new_nick == self.nick: # We changed our nick + s = _('You are now known as %s') % new_nick + else: + s = _('%s is now known as %s') % (nick, new_nick) + # We add new nick to muc roster here, so we don't see + # that "new_nick has joined the room" when he just changed nick. + # add_contact_to_roster will be called a second time + # after that, but that doesn't hurt + self.add_contact_to_roster(new_nick, show, role, affiliation, + status, jid) + if nick in self.attention_list: + self.attention_list.remove(nick) + # keep nickname color + if nick in self.gc_custom_colors: + self.gc_custom_colors[new_nick] = self.gc_custom_colors[nick] + # rename vcard / avatar + puny_jid = helpers.sanitize_filename(self.room_jid) + puny_nick = helpers.sanitize_filename(nick) + puny_new_nick = helpers.sanitize_filename(new_nick) + old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick) + new_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_new_nick) + files = {old_path: new_path} + path = os.path.join(gajim.AVATAR_PATH, puny_jid) + # possible extensions + for ext in ('.png', '.jpeg', '_notif_size_bw.png', + '_notif_size_colored.png'): + files[os.path.join(path, puny_nick + ext)] = \ + os.path.join(path, puny_new_nick + ext) + for old_file in files: + if os.path.exists(old_file): + if os.path.exists(files[old_file]): + # Windows require this + os.remove(files[old_file]) + os.rename(old_file, files[old_file]) + self.print_conversation(s, 'info', tim) + elif '321' in statusCode: + s = _('%(nick)s has been removed from the room (%(reason)s)') % { + 'nick': nick, 'reason': _('affiliation changed') } + self.print_conversation(s, 'info', tim = tim) + elif '322' in statusCode: + s = _('%(nick)s has been removed from the room (%(reason)s)') % { 'nick': nick, - 'reason': reason } - else: - s = _('%(nick)s has been kicked by %(who)s: %(reason)s') % { + 'reason': _('room configuration changed to members-only') } + self.print_conversation(s, 'info', tim = tim) + elif '332' in statusCode: + s = _('%(nick)s has been removed from the room (%(reason)s)') % { 'nick': nick, - 'who': actor, - 'reason': reason } - self.print_conversation(s, 'info', tim = tim) - elif statusCode == '301': - if actor is None: # do not print 'banned by None' - s = _('%(nick)s has been banned: %(reason)s') % { - 'nick': nick, - 'reason': reason } - else: - s = _('%(nick)s has been banned by %(who)s: %(reason)s') % { - 'nick': nick, - 'who': actor, - 'reason': reason } - self.print_conversation(s, 'info', tim = tim) - elif statusCode == '303': # Someone changed his or her nick - if nick == self.nick: # We changed our nick - self.nick = new_nick - s = _('You are now known as %s') % new_nick - else: - s = _('%s is now known as %s') % (nick, new_nick) - # We add new nick to muc roster here, so we don't see - # that "new_nick has joined the room" when he just changed nick. - # add_contact_to_roster will be called a second time - # after that, but that doesn't hurt - self.add_contact_to_roster(new_nick, show, role, affiliation, - status, jid) - # keep nickname color - if nick in self.gc_custom_colors: - self.gc_custom_colors[new_nick] = self.gc_custom_colors[nick] - # rename vcard / avatar - puny_jid = helpers.sanitize_filename(self.room_jid) - puny_nick = helpers.sanitize_filename(nick) - puny_new_nick = helpers.sanitize_filename(new_nick) - old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick) - new_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_new_nick) - files = {old_path: new_path} - path = os.path.join(gajim.AVATAR_PATH, puny_jid) - # possible extensions - for ext in ('.png', '.jpeg', '_notif_size_bw.png', - '_notif_size_colored.png'): - files[os.path.join(path, puny_nick + ext)] = \ - os.path.join(path, puny_new_nick + ext) - for old_file in files: - if os.path.exists(old_file): - if os.path.exists(files[old_file]): - # Windows require this - os.remove(files[old_file]) - os.rename(old_file, files[old_file]) - self.print_conversation(s, 'info', tim) - elif statusCode == 'destroyed': # Room has been destroyed - self.print_conversation(reason, 'info', tim) + 'reason': _('system shutdown') } + self.print_conversation(s, 'info', tim = tim) + elif 'destroyed' in statusCode: # Room has been destroyed + self.print_conversation(reason, 'info', tim) if len(gajim.events.get_events(self.account, fake_jid)) == 0: self.remove_contact(nick) @@ -934,7 +986,8 @@ class GroupchatControl(ChatControlBase): c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick) c.show = show c.status = status - if nick == self.nick and statusCode != '303': # We became offline + if nick == self.nick and (not statusCode or \ + '303' not in statusCode): # We became offline self.got_disconnected() contact = gajim.contacts.\ get_contact_with_highest_priority(self.account, self.room_jid) @@ -948,7 +1001,7 @@ class GroupchatControl(ChatControlBase): iter = self.add_contact_to_roster(nick, show, role, affiliation, status, jid) newly_created = True - if statusCode == '201': # We just created the room + if statusCode and '201' in statusCode: # We just created the room gajim.connections[self.account].request_gc_config(self.room_jid) else: gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, @@ -965,7 +1018,9 @@ class GroupchatControl(ChatControlBase): real_jid = fake_jid if con.vcard_shas.has_key(fake_jid): if avatar_sha != con.vcard_shas[fake_jid]: - con.request_vcard(real_jid, fake_jid) + server = gajim.get_server_from_jid(self.room_jid) + if not server.startswith('irc'): + con.request_vcard(real_jid, fake_jid) else: cached_vcard = con.get_cached_vcard(fake_jid, True) if cached_vcard and cached_vcard.has_key('PHOTO') and \ @@ -976,7 +1031,9 @@ class GroupchatControl(ChatControlBase): if cached_sha != avatar_sha: # avatar has been updated # sha in mem will be updated later - con.request_vcard(real_jid, fake_jid) + server = gajim.get_server_from_jid(self.room_jid) + if not server.startswith('irc'): + con.request_vcard(real_jid, fake_jid) else: # save sha in mem NOW con.vcard_shas[fake_jid] = avatar_sha @@ -997,7 +1054,8 @@ class GroupchatControl(ChatControlBase): if self.parent_win: self.parent_win.redraw_tab(self) if (time.time() - self.room_creation) > 30 and \ - nick != self.nick and statusCode != '303': + nick != self.nick and (not statusCode or \ + '303' not in statusCode): st = '' print_status = None for bookmark in gajim.connections[self.account].bookmarks: @@ -1011,8 +1069,11 @@ class GroupchatControl(ChatControlBase): # delete ressource simple_jid = gajim.get_jid_without_resource(jid) nick_jid += ' (%s)' % simple_jid + if show == 'offline': + if nick in self.attention_list: + self.attention_list.remove(nick) if show == 'offline' and print_status in ('all', 'in_and_out') and \ - statusCode != '307': + (not statusCode or '307' not in statusCode): st = _('%s has left') % nick_jid if reason: st += ' [%s]' % reason @@ -1127,6 +1188,7 @@ class GroupchatControl(ChatControlBase): nick = message_array[0] nick = helpers.parse_resource(nick) gajim.connections[self.account].join_gc(nick, self.room_jid, None) + self.nick = nick self.clear(self.msg_textview) else: self.get_command_help(command) @@ -1408,9 +1470,31 @@ class GroupchatControl(ChatControlBase): nick = instance.input_entry.get_text().decode('utf-8') nick = helpers.parse_resource(nick) gajim.connections[self.account].join_gc(nick, self.room_jid, None) + self.nick = nick instance = dialogs.InputDialog(title, prompt, proposed_nick, is_modal = False, ok_handler = on_ok) + def minimize(self, status='offline'): + # Minimize it + win = gajim.interface.msg_win_mgr.get_window(self.contact.jid, + self.account) + ctrl = win.get_control(self.contact.jid, self.account) + + ctrl_page = win.notebook.page_num(ctrl.widget) + control = win.notebook.get_nth_page(ctrl_page) + + win.notebook.remove_page(ctrl_page) + control.unparent() + ctrl.parent_win = None + + gajim.interface.minimized_controls[self.account][self.contact.jid] = \ + ctrl + + del win._controls[self.account][self.contact.jid] + + gajim.interface.roster.add_groupchat_to_roster(self.account, + self.contact.jid, status = self.subject) + def shutdown(self, status='offline'): gajim.connections[self.account].send_gc_status(self.nick, self.room_jid, show='offline', status=status) @@ -1441,13 +1525,15 @@ class GroupchatControl(ChatControlBase): gajim.events.remove_events(self.account, self.room_jid) def allow_shutdown(self, method): - '''If check_selection is True, ''' + if self.contact.jid in gajim.config.get_per('accounts', self.account, + 'minimized_gc').split(' '): + return 'minimize' if method == self.parent_win.CLOSE_ESC: model, iter = self.list_treeview.get_selection().get_selected() if iter: self.list_treeview.get_selection().unselect_all() - return False - retval = True + return 'no' + retval = 'yes' includes = gajim.config.get('confirm_close_muc_rooms').split(' ') excludes = gajim.config.get('noconfirm_close_muc_rooms').split(' ') # whether to ask for comfirmation before closing muc @@ -1463,7 +1549,7 @@ class GroupchatControl(ChatControlBase): _('Do _not ask me again')) if dialog.get_response() != gtk.RESPONSE_OK: - retval = False + retval = 'no' if dialog.is_checked(): # user does not want to be asked again gajim.config.set('confirm_close_muc', False) @@ -1546,6 +1632,7 @@ class GroupchatControl(ChatControlBase): 'name': self.name, 'jid': self.room_jid, 'autojoin': '0', + 'minimize': '0', 'password': '', 'nick': self.nick } @@ -1566,8 +1653,25 @@ class GroupchatControl(ChatControlBase): _('Bookmark has been added successfully'), _('You can manage your bookmarks via Actions menu in your roster.')) + def _on_drag_data_received(self, widget, context, x, y, selection, + target_type, timestamp): + # Invite contact to groupchat + treeview = gajim.interface.roster.tree + model = treeview.get_model() + if not selection.data: + return + data = selection.data + path = treeview.get_selection().get_selected_rows()[1][0] + iter = model.get_iter(path) + type = model[iter][2] + account = model[iter][4].decode('utf-8') + if type != 'contact': # source is not a contact + return + contact_jid = data.decode('utf-8') + gajim.connections[self.account].send_invite(self.room_jid, contact_jid) + def handle_message_textview_mykey_press(self, widget, event_keyval, - event_keymod): + event_keymod): # NOTE: handles mykeypress which is custom signal connected to this # CB in new_room(). for this singal see message_textview.py @@ -1800,13 +1904,21 @@ class GroupchatControl(ChatControlBase): item = xml.get_widget('add_to_roster_menuitem') if not jid: item.set_sensitive(False) - id = item.connect('activate', self.on_add_to_roster, jid) - self.handlers[id] = item + else: + id = item.connect('activate', self.on_add_to_roster, jid) + self.handlers[id] = item item = xml.get_widget('send_private_message_menuitem') id = item.connect('activate', self.on_send_pm, model, iter) self.handlers[id] = item + item = xml.get_widget('send_file_menuitem') + if not c.resource: + item.set_sensitive(False) + else: + id = item.connect('activate', self.on_send_file, c) + self.handlers[id] = item + # show the popup now! menu = xml.get_widget('gc_occupants_menu') menu.show_all() @@ -1835,7 +1947,7 @@ class GroupchatControl(ChatControlBase): else: # We want to send a private message nick = model[path][C_NICK].decode('utf-8') self._start_private_message(nick) - + def on_list_treeview_row_activated(self, widget, path, col = 0): '''When an iter is double clicked: open the chat window''' if not gajim.single_click: diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py index a833ee4bd..3622adc8e 100644 --- a/src/gtkgui_helpers.py +++ b/src/gtkgui_helpers.py @@ -584,9 +584,8 @@ def make_python_month_gtk_month(month): def make_color_string(color): '''create #aabbcc color string from gtk color''' col = '#' - for i in (color.red, color.green, color.blue): - # GTK sometime return a value > 256 - h = hex(i%256)[2:4] + for i in ('red', 'green', 'blue'): + h = hex(getattr(color, i) / (16*16)).split('x')[1] if len(h) == 1: h = '0' + h col += h diff --git a/src/history_manager.py b/src/history_manager.py index c861d395d..569d0e8bd 100755 --- a/src/history_manager.py +++ b/src/history_manager.py @@ -76,10 +76,7 @@ class HistoryManager: self.search_results_scrolledwindow = xml.get_widget( 'search_results_scrolledwindow') self.welcome_label = xml.get_widget('welcome_label') - - self.logs_scrolledwindow.set_no_show_all(True) - self.search_results_scrolledwindow.set_no_show_all(True) - + self.jids_already_in = [] # holds jids that we already have in DB self.AT_LEAST_ONE_DELETION_DONE = False diff --git a/src/message_control.py b/src/message_control.py index e2d16a45b..1bc2ac0dd 100644 --- a/src/message_control.py +++ b/src/message_control.py @@ -34,7 +34,7 @@ class MessageControl: self.widget_name = widget_name self.contact = contact self.account = account - self.hide_chat_buttons_current = False + self.hide_chat_buttons = False self.resource = resource gajim.last_message_time[self.account][self.get_full_jid()] = 0 @@ -56,9 +56,9 @@ class MessageControl: def allow_shutdown(self, method): '''Called to check is a control is allowed to shutdown. If a control is not in a suitable shutdown state this method - should return False''' + should return 'no', else 'yes' or 'minimize' ''' # NOTE: Derived classes MAY implement this - return True + return 'yes' def shutdown(self): # NOTE: Derived classes MUST implement this @@ -99,7 +99,7 @@ class MessageControl: def chat_buttons_set_visible(self, state): # NOTE: Derived classes MAY implement this - self.hide_chat_buttons_current = state + self.hide_chat_buttons = state def got_connected(self): pass @@ -111,7 +111,7 @@ class MessageControl: return len(gajim.events.get_events(self.account, self.contact.jid)) def send_message(self, message, keyID = '', type = 'chat', - chatstate = None, msg_id = None, composing_jep = None, resource = None, + chatstate = None, msg_id = None, composing_xep = None, resource = None, user_nick = None): '''Send the given message to the active tab. Doesn't return None if error ''' @@ -119,5 +119,5 @@ class MessageControl: # Send and update history return gajim.connections[self.account].send_message(jid, message, keyID, type = type, chatstate = chatstate, msg_id = msg_id, - composing_jep = composing_jep, resource = self.resource, + composing_xep = composing_xep, resource = self.resource, user_nick = user_nick) diff --git a/src/message_window.py b/src/message_window.py index 6445495be..65d25968c 100644 --- a/src/message_window.py +++ b/src/message_window.py @@ -103,13 +103,13 @@ class MessageWindow: self.notebook.set_show_tabs(False) self.notebook.set_show_border(gajim.config.get('tabs_border')) - # set up DnD - self.hid = self.notebook.connect('drag_data_received', - self.on_tab_label_drag_data_received_cb) - self.handlers[self.hid] = self.notebook - - self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS, - gtk.gdk.ACTION_MOVE) + # if GTK+ version < 2.10, use OUR way to reorder tabs (set up DnD) + if gtk.pygtk_version < (2, 10, 0) or gtk.gtk_version < (2, 10, 0): + self.hid = self.notebook.connect('drag_data_received', + self.on_tab_label_drag_data_received_cb) + self.handlers[self.hid] = self.notebook + self.notebook.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.DND_TARGETS, + gtk.gdk.ACTION_MOVE) def change_account_name(self, old_name, new_name): if self._controls.has_key(old_name): @@ -146,9 +146,16 @@ class MessageWindow: def _on_window_delete(self, win, event): # Make sure all controls are okay with being deleted + ctrl_to_minimize = [] for ctrl in self.controls(): - if not ctrl.allow_shutdown(self.CLOSE_CLOSE_BUTTON): + allow_shutdown = ctrl.allow_shutdown(self.CLOSE_CLOSE_BUTTON) + if allow_shutdown == 'no': return True # halt the delete + elif allow_shutdown == 'minimize': + ctrl_to_minimize.append(ctrl) + # If all are ok, minimize the one that need to be minimized + for ctrl in ctrl_to_minimize: + ctrl.minimize() return False def _on_window_destroy(self, win): @@ -191,7 +198,11 @@ class MessageWindow: control.handlers[id] = tab_label_box self.notebook.append_page(control.widget, tab_label_box) - self.setup_tab_dnd(control.widget) + # If GTK+ version >= 2.10, use gtk native way to reorder tabs + if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0): + self.notebook.set_tab_reorderable(control.widget, True) + else: + self.setup_tab_dnd(control.widget) self.redraw_tab(control) self.window.show_all() @@ -301,7 +312,12 @@ class MessageWindow: '''reason is only for gc (offline status message) if force is True, do not ask any confirmation''' # Shutdown the MessageControl - if not force and not ctrl.allow_shutdown(method): + allow_shutdown = ctrl.allow_shutdown(method) + if not force and allow_shutdown == 'no': + return + if allow_shutdown == 'minimize' and method != self.CLOSE_COMMAND: + ctrl.minimize() + self.check_tabs() return if reason is not None: # We are leaving gc with a status message ctrl.shutdown(reason) @@ -313,7 +329,10 @@ class MessageWindow: types = ['printed_msg', 'chat', 'gc_msg']) del gajim.last_message_time[ctrl.account][ctrl.get_full_jid()] - self.disconnect_tab_dnd(ctrl.widget) + # Disconnect tab DnD only if GTK version < 2.10 + if gtk.pygtk_version < (2, 10, 0) or gtk.gtk_version < (2, 10, 0): + self.disconnect_tab_dnd(ctrl.widget) + self.notebook.remove_page(self.notebook.page_num(ctrl.widget)) fjid = ctrl.get_full_jid() @@ -533,7 +552,7 @@ class MessageWindow: (event.state & gtk.gdk.MOD1_MASK): # ALT + 1,2,3.. self.notebook.set_current_page(st.index(event.string)) elif event.keyval == gtk.keysyms.c: # ALT + C toggles chat buttons - ctrl.chat_buttons_set_visible(not ctrl.hide_chat_buttons_current) + ctrl.chat_buttons_set_visible(not ctrl.hide_chat_buttons) # Close tab bindings elif event.keyval == gtk.keysyms.Escape and \ gajim.config.get('escape_key_closes'): # Escape diff --git a/src/music_track_listener.py b/src/music_track_listener.py index aad6f803a..2b332ae5f 100644 --- a/src/music_track_listener.py +++ b/src/music_track_listener.py @@ -61,6 +61,8 @@ class MusicTrackListener(gobject.GObject): 'NameOwnerChanged', 'org.freedesktop.DBus', arg0='org.gnome.Rhythmbox') bus.add_signal_receiver(self._player_playing_changed_cb, 'playingChanged', 'org.gnome.Rhythmbox.Player') + bus.add_signal_receiver(self._player_playing_song_property_changed_cb, + 'playingSongPropertyChanged', 'org.gnome.Rhythmbox.Player') ## Banshee banshee_bus = dbus.SessionBus() @@ -77,7 +79,7 @@ class MusicTrackListener(gobject.GObject): # Otherwise, it opens Banshee! self.banshee_props ={} gobject.timeout_add(1000, self._banshee_check_track_status) - + def _check_if_banshee_bus(self): if self.dubus_methods.NameHasOwner('org.gnome.Banshee'): self._get_banshee_bus() @@ -86,7 +88,6 @@ class MusicTrackListener(gobject.GObject): self.banshee_is_here = False return True - def _get_banshee_bus(self): bus = dbus.SessionBus() banshee = bus.get_object('org.gnome.Banshee', '/org/gnome/Banshee/Player') @@ -106,6 +107,10 @@ class MusicTrackListener(gobject.GObject): else: self.emit('music-track-changed', None) + def _player_playing_song_property_changed_cb(self, a, b, c, d): + if b == 'rb:stream-song-title': + self.emit('music-track-changed', self._last_playing_music) + def _muine_properties_extract(self, song_string): d = dict((x.strip() for x in s1.split(':', 1)) for s1 in \ song_string.split('\n')) diff --git a/src/network_manager_listener.py b/src/network_manager_listener.py index aeeb13a34..5c5cba42d 100644 --- a/src/network_manager_listener.py +++ b/src/network_manager_listener.py @@ -27,23 +27,25 @@ def device_no_longer_active(self, *args): 'listen_to_network_manager') and connection.connected > 1: connection._disconnectedReconnCB() +supported = False -from common.dbus_support import system_bus +try: + from common.dbus_support import system_bus -import dbus -import dbus.glib + bus = system_bus.SystemBus() -bus = system_bus.SystemBus() - -bus.add_signal_receiver(device_no_longer_active, - 'DeviceNoLongerActive', - 'org.freedesktop.NetworkManager', - 'org.freedesktop.NetworkManager', - '/org/freedesktop/NetworkManager') - -bus.add_signal_receiver(device_now_active, - 'DeviceNowActive', - 'org.freedesktop.NetworkManager', - 'org.freedesktop.NetworkManager', - '/org/freedesktop/NetworkManager') + if 'org.freedesktop.NetworkManager' in bus.list_names(): + supported = True + bus.add_signal_receiver(device_no_longer_active, + 'DeviceNoLongerActive', + 'org.freedesktop.NetworkManager', + 'org.freedesktop.NetworkManager', + '/org/freedesktop/NetworkManager') + bus.add_signal_receiver(device_now_active, + 'DeviceNowActive', + 'org.freedesktop.NetworkManager', + 'org.freedesktop.NetworkManager', + '/org/freedesktop/NetworkManager') +except: + pass diff --git a/src/notify.py b/src/notify.py index 66b130791..36555b04c 100644 --- a/src/notify.py +++ b/src/notify.py @@ -154,7 +154,11 @@ def notify(event, jid, account, parameters, advanced_notif_num = None): message_type = parameters[0] is_first_message = parameters[1] nickname = parameters[2] - message = parameters[3] + if gajim.config.get('notification_preview_message'): + message = parameters[3] + else: + # We don't want message preview, do_preview = False + message = '' if helpers.allow_showing_notification(account, 'notify_on_new_message', advanced_notif_num, is_first_message): do_popup = True @@ -235,8 +239,12 @@ def notify(event, jid, account, parameters, advanced_notif_num = None): img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'priv_msg_recv.png') title = _('New Private Message from group chat %s') % room_name - text = _('%(nickname)s: %(message)s') % {'nickname': nickname, - 'message': message} + if message: + text = _('%(nickname)s: %(message)s') % {'nickname': nickname, + 'message': message} + else: + text = _('Messaged by %(nickname)s') % {'nickname': nickname} + else: # chat message event_type = _('New Message') img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', @@ -299,7 +307,8 @@ def popup(event_type, jid, account, msg_type = '', path_to_image = None, gajim.log.debug(str(e)) # we failed to speak to notification daemon via D-Bus if USER_HAS_PYNOTIFY: # try via libnotify - if not text: + if not text and event_type == 'new_message': + # empty text for new_message means do_preview = False text = gajim.get_name_from_jid(account, jid) # default value of text if not title: title = event_type @@ -408,8 +417,9 @@ class DesktopNotification: self.jid = jid self.msg_type = msg_type - if not text: - # default value of text + # default value of text + if not text and event_type == 'new_message': + # empty text for new_message means do_preview = False self.text = gajim.get_name_from_jid(account, jid) if not title: diff --git a/src/profile_window.py b/src/profile_window.py index b56329aa4..94a4cde4f 100644 --- a/src/profile_window.py +++ b/src/profile_window.py @@ -77,10 +77,6 @@ class ProfileWindow: # Create Image for avatar button image = gtk.Image() self.xml.get_widget('PHOTO_button').set_image(image) - text_button = self.xml.get_widget('NOPHOTO_button') - # We use 2 buttons because some GTK theme don't show images in buttons - text_button.set_no_show_all(True) - text_button.hide() self.xml.signal_autoconnect(self) self.window.show_all() diff --git a/src/remote_control.py b/src/remote_control.py index c9583f50f..e6712e8b5 100644 --- a/src/remote_control.py +++ b/src/remote_control.py @@ -4,6 +4,8 @@ ## Copyright (C) 2005-2006 Nikos Kouremenos ## Copyright (C) 2005-2006 Dimitur Kirov ## Copyright (C) 2005-2006 Andrew Sayman +## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -223,6 +225,31 @@ class SignalObject(dbus.service.Object): return connected_account, contact + def _get_account_for_groupchat(self, account, room_jid): + '''get the account which is connected to groupchat (if not given) + or check if the given account is connected to the groupchat''' + connected_account = None + accounts = gajim.contacts.get_accounts() + # if there is only one account in roster, take it as default + # if user did not ask for account + if not account and len(accounts) == 1: + account = accounts[0] + if account: + if gajim.connections[account].connected > 1 and \ + room_jid in gajim.gc_connected[account] and \ + gajim.gc_connected[account][room_jid]: + # account and groupchat are connected + connected_account = account + else: + for account in accounts: + if gajim.connections[account].connected > 1 and \ + room_jid in gajim.gc_connected[account] and \ + gajim.gc_connected[account][room_jid]: + # account and groupchat are connected + connected_account = account + break + return connected_account + @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b') def send_file(self, file_path, jid, account): '''send file, located at 'file_path' to 'jid', using account @@ -269,6 +296,19 @@ class SignalObject(dbus.service.Object): jid = self._get_real_jid(jid, account) return self._send_message(jid, message, keyID, account, type, subject) + @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b') + def send_groupchat_message(self, room_jid, message, account): + '''Send 'message' to groupchat 'room_jid', + using account (optional) 'account'.''' + if not room_jid or not message: + return DBUS_BOOLEAN(False) + connected_account = self._get_account_for_groupchat(account, room_jid) + if connected_account: + connection = gajim.connections[connected_account] + connection.send_gc_message(room_jid, message) + return DBUS_BOOLEAN(True) + return DBUS_BOOLEAN(False) + @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b') def open_chat(self, jid, account): '''Shows the tabbed window for new message to 'jid', using account @@ -410,7 +450,7 @@ class SignalObject(dbus.service.Object): if acct in accounts: for jid in gajim.contacts.get_jid_list(acct): item = self._contacts_as_dbus_structure( - gajim.contacts.get_contact(acct, jid)) + gajim.contacts.get_contacts(acct, jid)) if item: result.append(item) return result @@ -503,7 +543,7 @@ class SignalObject(dbus.service.Object): accounts = [account] contact_exists = False for account in accounts: - contacts = gajim.contacts.get_contact(account, jid) + contacts = gajim.contacts.get_contacts(account, jid) if contacts: gajim.connections[account].unsubscribe(jid) for contact in contacts: @@ -531,12 +571,12 @@ class SignalObject(dbus.service.Object): nick_in_roster = None # Is jid a nick ? for account in accounts: # Does jid exists in roster of one account ? - if gajim.contacts.get_contacts_from_jid(account, jid): + if gajim.contacts.get_contacts(account, jid): return jid if not nick_in_roster: # look in all contact if one has jid as nick for jid_ in gajim.contacts.get_jid_list(account): - c = gajim.contacts.get_contacts_from_jid(account, jid_) + c = gajim.contacts.get_contacts(account, jid_) if c[0].name == jid: nick_in_roster = jid_ break diff --git a/src/roster_window.py b/src/roster_window.py index 59f65d620..ebaa3cd31 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -4,6 +4,8 @@ ## Copyright (C) 2003-2006 Yann Le Boulanger ## Copyright (C) 2005-2007 Nikos Kouremenos ## Copyright (C) 2005-2006 Dimitur Kirov +## Copyright (C) 2007 Lukas Petrovicky +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -16,6 +18,7 @@ ## import gtk +import pango import gobject import os import time @@ -33,6 +36,7 @@ import tooltips import message_control import adhoc_commands import notify +import features_window from common import gajim from common import helpers @@ -64,6 +68,7 @@ class RosterWindow: '''Class for main window of the GTK+ interface''' def get_account_iter(self, name): + ''' Returns a gtk.TreeIter of accounts in roster data model or None ''' model = self.tree.get_model() if model is None: return @@ -78,6 +83,7 @@ class RosterWindow: return account_iter def get_group_iter(self, name, account): + ''' Returns a gtk.TreeIter of groups in roster data model or None ''' model = self.tree.get_model() root = self.get_account_iter(account) group_iter = model.iter_children(root) @@ -143,8 +149,8 @@ class RosterWindow: path = None return path - def show_and_select_path(self, path, jid, account): - '''Show contact in roster (if he is invisible for example) + def show_and_select_path(self, path, jid, account): + '''Show contact in roster (if he is invisible for example) and select line''' if not path: # contact is in roster but we curently don't see him online @@ -163,10 +169,12 @@ class RosterWindow: self.tree.set_cursor(path) def add_account_to_roster(self, account): + ''' Add an account to roster data model. ''' model = self.tree.get_model() if self.get_account_iter(account): return + # if we merge accounts... if self.regroup: show = helpers.get_global_show() model.append(None, [self.jabber_state_images['16'][show], @@ -234,7 +242,7 @@ class RosterWindow: gajim.get_number_of_connected_accounts())) and gajim.config.get( 'show_contacts_number'): nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts( - accounts = accounts) + accounts = accounts) account_name += ' (%s/%s)' % (repr(nbr_on),repr(nbr_total)) model[iter][C_NAME] = account_name @@ -254,7 +262,7 @@ class RosterWindow: contact = gajim.contacts.get_first_contact_from_jid(account, jid) nb_events = gajim.events.get_nb_roster_events(account, contact.jid) # count events from all resources - for contact_ in gajim.contacts.get_contact(account, jid): + for contact_ in gajim.contacts.get_contacts(account, jid): if contact_.resource: nb_events += gajim.events.get_nb_roster_events(account, contact_.get_full_jid()) @@ -293,7 +301,7 @@ class RosterWindow: family = gajim.contacts.get_metacontacts_family(account, jid) # family members that are in roster and belong to the same account. - shown_family = [] + shown_family = [] if family: for data in family: _account = data['account'] @@ -301,7 +309,7 @@ class RosterWindow: if _account != account and not self.regroup: continue _jid = data['jid'] - + if self.get_contact_iter(_jid, _account): shown_family.append(data) if _jid == jid and _account == account: @@ -331,14 +339,14 @@ class RosterWindow: if (contact.show in ('offline', 'error') or hide) and \ not showOffline and (not _('Transports') in contact.groups or \ gajim.connections[account].connected < 2) and \ - len(gajim.contacts.get_contact(account, jid)) == 1 and nb_events == 0 and\ + len(gajim.contacts.get_contacts(account, jid)) == 1 and nb_events == 0 and\ not _('Not in Roster') in contact.groups: return # Remove brother contacts that are already in roster to add them # under this iter for data in shown_family: - contacts = gajim.contacts.get_contact(data['account'], + contacts = gajim.contacts.get_contacts(data['account'], data['jid']) for c in contacts: self.remove_contact(c, data['account']) @@ -348,6 +356,7 @@ class RosterWindow: elif not groups: groups = [_('General')] for group in groups: + self.draw_group(group, account) iterG = self.get_group_iter(group, account) if not iterG: IterAcct = self.get_account_iter(account) @@ -370,7 +379,7 @@ class RosterWindow: typestr = 'contact' if group == _('Transports'): typestr = 'agent' - if group == _('Groupchats'): + if gajim.gc_connected[account].has_key(jid): typestr = 'groupchat' name = contact.get_shown_name() @@ -383,7 +392,7 @@ class RosterWindow: self.draw_avatar(jid, account) # put the children under this iter for data in shown_family: - contacts = gajim.contacts.get_contact(data['account'], + contacts = gajim.contacts.get_contacts(data['account'], data['jid']) self.add_contact_to_roster(data['jid'], data['account']) @@ -426,12 +435,19 @@ class RosterWindow: contact = gajim.contacts.get_contact_with_highest_priority(account, jid) if contact == None: contact = gajim.contacts.create_contact(jid = jid, name = jid, - groups = [_('Groupchats')], show = 'muc_active', + groups = [_('Groupchats')], show = 'online', status = status, sub = 'none', resource = resource) gajim.contacts.add_contact(account, contact) self.add_contact_to_roster(jid, account) - self.draw_group(_('Groupchats'), account) + self.draw_group(_('Groupchats'), account) + else: + contact.show = 'online' + self.draw_contact(jid, account) + self.add_contact_to_roster(jid, account) + for group in contact.groups: + self.draw_group(group, account) + self.draw_account(account) return contact def get_self_contact_iter(self, account): @@ -485,8 +501,8 @@ class RosterWindow: return if contact.jid in gajim.to_be_removed[account]: gajim.to_be_removed[account].remove(contact.jid) - - + + hide = contact.is_hidden_from_roster() show_offline = gajim.config.get('showoffline') @@ -526,7 +542,7 @@ class RosterWindow: c_account = model[child_iter][C_ACCOUNT].decode('utf-8') children.append((c_jid, c_account)) child_iter = model.iter_next(child_iter) - + # Remove iters and group iter if they are empty for i in iters: parent_i = model.iter_parent(i) @@ -543,6 +559,8 @@ class RosterWindow: else: if gajim.groups[account].has_key(group): del gajim.groups[account][group] + else: + self.draw_group(group, account) # re-add children for child in children: @@ -573,7 +591,7 @@ class RosterWindow: iters = self.get_contact_iter(jid, account) if len(iters) == 0: return - contact_instances = gajim.contacts.get_contact(account, jid) + contact_instances = gajim.contacts.get_contacts(account, jid) contact = gajim.contacts.get_highest_prio_contact_from_contacts( contact_instances) if not contact: @@ -581,8 +599,7 @@ class RosterWindow: name = gobject.markup_escape_text(contact.get_shown_name()) # gets number of unread gc marked messages - if gajim.interface.minimized_controls.has_key(account) and \ - jid in gajim.interface.minimized_controls[account]: + if jid in gajim.interface.minimized_controls[account]: nb_unread = len(gajim.events.get_events(account, jid, ['printed_marked_gc_msg'])) nb_unread += \ @@ -645,22 +662,28 @@ class RosterWindow: status = helpers.reduce_chars_newlines(status, max_lines = 1) # escape markup entities and make them small italic and fg color color = gtkgui_helpers._get_fade_color(self.tree, selected, focus) - colorstring = "#%04x%04x%04x" % (color.red, color.green, color.blue) + colorstring = '#%04x%04x%04x' % (color.red, color.green, color.blue) name += \ '\n%s' \ % (colorstring, gobject.markup_escape_text(status)) iter = iters[0] # choose the icon with the first iter + + if gajim.gc_connected[account].has_key(jid): + contact.show = 'online' + model[iter][C_TYPE] = 'groupchat' + icon_name = helpers.get_icon_name_to_show(contact, account) # look if another resource has awaiting events for c in contact_instances: c_icon_name = helpers.get_icon_name_to_show(c, account) - if c_icon_name == 'message': + if c_icon_name in ('event', 'muc_active', 'muc_inactive'): icon_name = c_icon_name break path = model.get_path(iter) if model.iter_has_child(iter): - if not self.tree.row_expanded(path) and icon_name != 'message': + if not self.tree.row_expanded(path) and \ + icon_name not in ('event', 'muc_active', 'muc_inactive'): child_iter = model.iter_children(iter) if icon_name in ('error', 'offline'): # get the icon from the first child as they are sorted by show @@ -677,7 +700,7 @@ class RosterWindow: child_jid = model[child_iter][C_JID].decode('utf-8') child_account = model[child_iter][C_ACCOUNT].decode('utf-8') if len(gajim.events.get_events(child_account, child_jid)): - icon_name = 'message' + icon_name = 'event' break child_iter = model.iter_next(child_iter) if self.tree.row_expanded(path): @@ -691,13 +714,6 @@ class RosterWindow: self.draw_parent_contact(jid, account) state_images = self.get_appropriate_state_images(jid, icon_name = icon_name) - - if icon_name != 'message' and gajim.gc_connected[account].\ - has_key(jid): - if gajim.gc_connected[account][jid]: - icon_name = 'muc_active' - else: - icon_name = 'muc_inactive' img = state_images[icon_name] @@ -742,44 +758,46 @@ class RosterWindow: win.set_active_tab(room_jid, account) dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid) return - if gajim.interface.minimized_controls.has_key(account) and \ - room_jid in gajim.interface.minimized_controls[account]: - self.on_groupchat_maximized(None, room_jid, account) - return + minimized_control_exists = False + if room_jid in gajim.interface.minimized_controls[account]: + minimized_control_exists = True invisible_show = gajim.SHOW_LIST.index('invisible') if gajim.connections[account].connected == invisible_show: dialogs.ErrorDialog( _('You cannot join a group chat while you are invisible')) return - if minimize: + if minimize and not minimized_control_exists and \ + not gajim.interface.msg_win_mgr.has_window(room_jid, account): contact = gajim.contacts.create_contact(jid = room_jid, name = nick) gc_control = GroupchatControl(None, contact, account) - - if not gajim.interface.minimized_controls.has_key(account): - gajim.interface.minimized_controls[account] = {} gajim.interface.minimized_controls[account][room_jid] = gc_control - - self.add_groupchat_to_roster(account, room_jid) gajim.connections[account].join_gc(nick, room_jid, password) if password: gajim.gc_passwords[room_jid] = password + self.add_groupchat_to_roster(account, room_jid) return - if not gajim.interface.msg_win_mgr.has_window(room_jid, account): + if not minimized_control_exists and \ + not gajim.interface.msg_win_mgr.has_window(room_jid, account): self.new_room(room_jid, nick, account) - gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account) - gc_win.set_active_tab(room_jid, account) - gc_win.window.present() + if not minimized_control_exists: + gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account) + gc_win.set_active_tab(room_jid, account) + gc_win.window.present() gajim.connections[account].join_gc(nick, room_jid, password) if password: gajim.gc_passwords[room_jid] = password + contact = gajim.contacts.get_contact_with_highest_priority(account, \ + room_jid) + if contact or minimized_control_exists: + self.add_groupchat_to_roster(account, room_jid) def on_actions_menuitem_activate(self, widget): self.make_menu() - + def on_edit_menuitem_activate(self, widget): '''need to call make_menu to build profile, avatar item''' self.make_menu() - + def on_bookmark_menuitem_activate(self, widget, account, bookmark): self.join_gc_room(account, bookmark['jid'], bookmark['nick'], bookmark['password']) @@ -802,7 +820,7 @@ class RosterWindow: else: gajim.interface.instances[account]['privacy_lists'] = \ dialogs.PrivacyListsWindow(account) - + def on_blocked_contacts_menuitem_activate(self, widget, account): if gajim.interface.instances[account].has_key('blocked_contacts'): gajim.interface.instances[account]['blocked_contacts'].window.present() @@ -874,7 +892,7 @@ class RosterWindow: else: send_single_message_menuitem.connect('activate', self.on_send_single_message_menuitem_activate, account) - + send_server_message_menuitem.connect('activate', self.on_send_server_message_menuitem_activate, account) @@ -952,15 +970,15 @@ class RosterWindow: gc_sub_menu = gtk.Menu() # gc is always a submenu join_gc_menuitem.set_submenu(gc_sub_menu) - + connected_accounts = gajim.get_number_of_connected_accounts() if connected_accounts > 1: # 2 or more accounts? make submenus add_sub_menu = gtk.Menu() disco_sub_menu = gtk.Menu() new_chat_sub_menu = gtk.Menu() - accounts_list = gajim.contacts.get_accounts() - accounts_list.sort() + accounts_list = gajim.contacts.get_accounts() + accounts_list.sort() for account in accounts_list: if gajim.connections[account].connected <= 1: # if offline or connecting @@ -973,20 +991,15 @@ class RosterWindow: new_chat_item.connect('activate', self.on_new_chat_menuitem_activate, account) - if gajim.config.get_per('accounts', account, 'is_zeroconf'): - continue + if gajim.config.get_per('accounts', account, 'is_zeroconf'): + continue # join gc - label = gtk.Label() - label.set_markup('' + account.upper() +'') - label.set_use_underline(False) - gc_item = gtk.MenuItem() - gc_item.add(label) - gc_item.connect('state-changed', - gtkgui_helpers.on_bm_header_changed_state) + gc_item = gtk.MenuItem(_('using account %s') % account, False) gc_sub_menu.append(gc_item) - - self.add_bookmarks_list(gc_sub_menu, account) + gc_menuitem_menu = gtk.Menu() + self.add_bookmarks_list(gc_menuitem_menu, account) + gc_item.set_submenu(gc_menuitem_menu) # the 'manage gc bookmarks' item is shown # below to avoid duplicate code @@ -1009,6 +1022,7 @@ class RosterWindow: disco_sub_menu.show_all() new_chat_menuitem.set_submenu(new_chat_sub_menu) new_chat_sub_menu.show_all() + gc_sub_menu.show_all() elif connected_accounts == 1: # user has only one account for account in gajim.connections: @@ -1036,9 +1050,9 @@ class RosterWindow: new_chat_menuitem.add_accelerator('activate', ag, gtk.keysyms.n, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.have_new_chat_accel = True - + break # No other account connected - + if connected_accounts == 0: # no connected accounts, make the menuitems insensitive for item in [new_chat_menuitem, join_gc_menuitem,\ @@ -1048,7 +1062,7 @@ class RosterWindow: for item in [new_chat_menuitem, join_gc_menuitem,\ add_new_contact_menuitem, service_disco_menuitem]: item.set_sensitive(True) - + # disable some fields if only local account is there if connected_accounts == 1: for account in gajim.connections: @@ -1085,6 +1099,12 @@ class RosterWindow: pep_services_sub_menu.append(pep_services_item) pep_services_item.connect('activate', self.on_pep_services_menuitem_activate, account) + # profile, avatar + profile_avatar_item = gtk.MenuItem(_('of account %s') % account, + False) + profile_avatar_sub_menu.append(profile_avatar_item) + profile_avatar_item.connect('activate', + self.on_profile_avatar_menuitem_activate, account) profile_avatar_menuitem.set_submenu(profile_avatar_sub_menu) profile_avatar_sub_menu.show_all() pep_services_menuitem.set_submenu(pep_services_sub_menu) @@ -1117,7 +1137,7 @@ class RosterWindow: self.on_manage_bookmarks_menuitem_activate) gc_sub_menu.append(newitem) gc_sub_menu.show_all() - + # Advanced Actions if len(gajim.connections) == 0: # user has no accounts advanced_menuitem.set_sensitive(False) @@ -1126,7 +1146,7 @@ class RosterWindow: advanced_menuitem_menu = self.get_and_connect_advanced_menuitem_menu( account) self.advanced_menus.append(advanced_menuitem_menu) - + self._add_history_manager_menuitem(advanced_menuitem_menu) advanced_menuitem.set_submenu(advanced_menuitem_menu) @@ -1144,20 +1164,20 @@ class RosterWindow: self.get_and_connect_advanced_menuitem_menu(account) self.advanced_menus.append(advanced_menuitem_menu) advanced_item.set_submenu(advanced_menuitem_menu) - + self._add_history_manager_menuitem(advanced_sub_menu) - + advanced_menuitem.set_submenu(advanced_sub_menu) advanced_sub_menu.show_all() self.actions_menu_needs_rebuild = False def _add_history_manager_menuitem(self, menu): - '''adds a seperator and History Manager menuitem BELOW for account + '''adds a seperator and History Manager menuitem BELOW for account menuitems''' item = gtk.SeparatorMenuItem() # separator menu.append(item) - + # History manager item = gtk.ImageMenuItem(_('History Manager')) icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, @@ -1165,12 +1185,18 @@ class RosterWindow: item.set_image(icon) menu.append(item) item.connect('activate', self.on_history_manager_menuitem_activate) - + def add_bookmarks_list(self, gc_sub_menu, account): '''Show join new group chat item and bookmarks list for an account''' - item = gtk.MenuItem(_('_Join New Group Chat')) + item = gtk.ImageMenuItem(_('_Join New Group Chat')) + icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU) + item.set_image(icon) item.connect('activate', self.on_join_gc_activate, account) gc_sub_menu.append(item) + + if len(gajim.connections[account].bookmarks) > 0: # user has at least one bookmark + item = gtk.SeparatorMenuItem() # separator + gc_sub_menu.append(item) for bookmark in gajim.connections[account].bookmarks: item = gtk.MenuItem(bookmark['name'], False) # Do not use underline @@ -1179,7 +1205,7 @@ class RosterWindow: gc_sub_menu.append(item) def _change_style(self, model, path, iter, option): - if option is None or model[iter][C_TYPE] == option: + if option is None or model[iter][C_TYPE] == option: # We changed style for this type of row model[iter][C_NAME] = model[iter][C_NAME] @@ -1198,6 +1224,8 @@ class RosterWindow: for acct in gajim.connections: self.add_account_to_roster(acct) self.add_account_contacts(acct) + # Recalculate column width for ellipsizing + self.tree.columns_autosize() def add_account_contacts(self, account): '''adds contacts of group to roster treeview''' @@ -1280,13 +1308,18 @@ class RosterWindow: def chg_contact_status(self, contact, show, status, account): '''When a contact changes his or her status''' - contact_instances = gajim.contacts.get_contact(account, contact.jid) + contact_instances = gajim.contacts.get_contacts(account, contact.jid) contact.show = show contact.status = status + # name is to show in conversation window + name = contact.get_shown_name() + if show in ('offline', 'error') and \ len(gajim.events.get_events(account, contact.get_full_jid())) == 0: if len(contact_instances) > 1: # if multiple resources + if contact.resource != '': + name += '/' + contact.resource jid_with_resource = contact.jid + '/' + contact.resource if gajim.interface.msg_win_mgr.has_window(jid_with_resource, account): @@ -1296,33 +1329,26 @@ class RosterWindow: ctrl.update_ui() win.redraw_tab(ctrl) gajim.contacts.remove_contact(account, contact) + elif len(contact_instances) > 1 and contact.resource != '': + name += '/' + contact.resource self.remove_contact(contact, account) self.add_contact_to_roster(contact.jid, account) # print status in chat window and update status/GPG image - jid_list = [contact.jid] - for jid in jid_list: - if gajim.interface.msg_win_mgr.has_window(jid, account): - win = gajim.interface.msg_win_mgr.get_window(jid, account) - ctrl = win.get_control(jid, account) - ctrl.contact = gajim.contacts.get_contact_with_highest_priority( - account, contact.jid) - ctrl.update_ui() - win.redraw_tab(ctrl) + if gajim.interface.msg_win_mgr.has_window(contact.jid, account): + win = gajim.interface.msg_win_mgr.get_window(contact.jid, account) + ctrl = win.get_control(contact.jid, account) + ctrl.contact = gajim.contacts.get_contact_with_highest_priority( + account, contact.jid) + ctrl.update_ui() + win.redraw_tab(ctrl) - name = contact.get_shown_name() - - # if multiple resources (or second one disconnecting) - if (len(contact_instances) > 1 or (len(contact_instances) == 1 and \ - show in ('offline', 'error'))) and contact.resource != '': - name += '/' + contact.resource - - uf_show = helpers.get_uf_show(show) - if status: - ctrl.print_conversation(_('%s is now %s (%s)') % (name, uf_show, - status), 'status') - else: # No status message - ctrl.print_conversation(_('%s is now %s') % (name, uf_show), - 'status') + uf_show = helpers.get_uf_show(show) + if status: + ctrl.print_conversation(_('%s is now %s (%s)') % (name, uf_show, + status), 'status') + else: # No status message + ctrl.print_conversation(_('%s is now %s') % (name, uf_show), + 'status') if not contact.groups: self.draw_group(_('General'), account) @@ -1330,7 +1356,7 @@ class RosterWindow: for group in contact.groups: self.draw_group(group, account) - self.draw_account(account) + self.draw_account(account) def on_info(self, widget, contact, account): '''Call vcard_information_window class to display contact's information''' @@ -1349,7 +1375,7 @@ class RosterWindow: if info.has_key(contact.jid): info[contact.jid].window.present() else: - contact = gajim.contacts.get_first_contact_from_jid(account, + contact = gajim.contacts.get_first_contact_from_jid(account, contact.jid) if contact.show in ('offline', 'error'): # don't show info on offline contacts @@ -1365,7 +1391,7 @@ class RosterWindow: if props and self.tooltip.id == props[0]: # bounding rectangle of coordinates for the cell within the treeview rect = self.tree.get_cell_area(props[0], props[1]) - + # position of the treeview on the screen position = self.tree.window.get_origin() self.tooltip.show_tooltip(contact, rect.height, position[1] + rect.y) @@ -1398,7 +1424,7 @@ class RosterWindow: jid = model[iter][C_JID].decode('utf-8') if self.tooltip.timeout == 0 or self.tooltip.id != props[0]: self.tooltip.id = row - contacts = gajim.contacts.get_contact(account, jid) + contacts = gajim.contacts.get_contacts(account, jid) connected_contacts = [] for c in contacts: if c.show not in ('offline', 'error'): @@ -1414,7 +1440,7 @@ class RosterWindow: jid = model[iter][C_JID].decode('utf-8') if self.tooltip.timeout == 0 or self.tooltip.id != props[0]: self.tooltip.id = row - contact = gajim.contacts.get_contact(account, jid) + contact = gajim.contacts.get_contacts(account, jid) self.tooltip.account = account self.tooltip.timeout = gobject.timeout_add(500, self.show_tooltip, contact) @@ -1558,7 +1584,7 @@ class RosterWindow: # needed for draw_contact: gajim.connections[account].blocked_contacts.append(contact.jid) self.draw_contact(contact.jid, account) - else: + else: group = model[iter][C_JID].decode('utf-8') for (contact, account) in group_list: if account not in accounts: @@ -1671,12 +1697,12 @@ class RosterWindow: gajim.connections[account].status, to=contact.jid) else: self.send_status(account, show, - gajim.connections[account].status, to=contact.jid) + gajim.connections[account].status, to=contact.jid) def on_rename(self, widget, iter, path): # this function is called either by F2 or by Rename menuitem if gajim.interface.instances.has_key('rename'): - gajim.interface.instances['rename'].dialog.window.present() + gajim.interface.instances['rename'].dialog.present() return model = self.tree.get_model() @@ -1705,7 +1731,7 @@ class RosterWindow: if row_type in ('contact', 'agent'): if old_text == new_text: return - for u in gajim.contacts.get_contact(account, jid): + for u in gajim.contacts.get_contacts(account, jid): u.name = new_text gajim.connections[account].update_contact(jid, new_text, u.groups) self.draw_contact(jid, account) @@ -1745,7 +1771,7 @@ class RosterWindow: def on_canceled(): if gajim.interface.instances.has_key('rename'): del gajim.interface.instances['rename'] - + gajim.interface.instances['rename'] = dialogs.InputDialog(title, message, old_text, False, (on_renamed, account, row_type, jid, old_text), on_canceled) @@ -1785,8 +1811,8 @@ class RosterWindow: contact.name, contact.groups) self.add_contact_to_roster(contact.jid, account) else: - gajim.connections[account].unsubscribe(contact.jid) - for c in gajim.contacts.get_contact(account, contact.jid): + gajim.connections[account].unsubscribe(contact.jid) + for c in gajim.contacts.get_contacts(account, contact.jid): self.remove_contact(c, account) gajim.contacts.remove_jid(account, c.jid) self.readd_if_needed(contact, account) @@ -1814,11 +1840,11 @@ class RosterWindow: if keyID[0] == _('None'): if contact.jid in keys: del keys[contact.jid] - for u in gajim.contacts.get_contact(account, contact.jid): + for u in gajim.contacts.get_contacts(account, contact.jid): u.keyID = '' else: keys[contact.jid] = keyID[0] - for u in gajim.contacts.get_contact(account, contact.jid): + for u in gajim.contacts.get_contacts(account, contact.jid): u.keyID = keyID[0] if gajim.interface.msg_win_mgr.has_window(contact.jid, account): ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account) @@ -1828,6 +1854,63 @@ class RosterWindow: keys_str += jid + ' ' + keys[jid] + ' ' gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str) + def update_avatar_in_gui(self, jid, account): + # Update roster + self.draw_avatar(jid, account) + # Update chat window + if gajim.interface.msg_win_mgr.has_window(jid, account): + win = gajim.interface.msg_win_mgr.get_window(jid, account) + ctrl = win.get_control(jid, account) + if win and ctrl.type_id != message_control.TYPE_GC: + ctrl.show_avatar() + + def on_set_custom_avatar_activate(self, widget, contact, account): + def on_ok(widget, path_to_file): + filesize = os.path.getsize(path_to_file) # in bytes + invalid_file = False + msg = '' + if os.path.isfile(path_to_file): + stat = os.stat(path_to_file) + if stat[6] == 0: + invalid_file = True + msg = _('File is empty') + else: + invalid_file = True + msg = _('File does not exist') + if invalid_file: + dialogs.ErrorDialog(_('Could not load image'), msg) + return + try: + pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file) + if filesize > 16384: # 16 kb + # get the image at 'notification size' + # and hope that user did not specify in ACE crazy size + pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip') + except gobject.GError, msg: # unknown format + # msg should be string, not object instance + msg = str(msg) + dialogs.ErrorDialog(_('Could not load image'), msg) + return + puny_jid = helpers.sanitize_filename(contact.jid) + path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png' + pixbuf.save(path_to_file, 'png') + dlg.destroy() + self.update_avatar_in_gui(contact.jid, account) + + def on_clear(widget): + dlg.destroy() + # Delete file: + puny_jid = helpers.sanitize_filename(contact.jid) + path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png' + try: + os.remove(path_to_file) + except OSError: + gajim.log.debug('Cannot remove %s' % path_to_file) + self.update_avatar_in_gui(contact.jid, account) + + dlg = dialogs.AvatarChooserDialog(on_response_ok = on_ok, + on_response_clear = on_clear) + def on_edit_groups(self, widget, list_): dlg = dialogs.EditGroupsDialog(list_) dlg.run() @@ -1840,6 +1923,21 @@ class RosterWindow: gajim.interface.instances['logs'][contact.jid] = history_window.\ HistoryWindow(contact.jid, account) + def on_disconnect(self, widget, jid, account): + '''When disconnect menuitem is activated: disconect from room''' + ctrl = gajim.interface.minimized_controls[account][jid] + del gajim.interface.minimized_controls[account][jid] + ctrl.shutdown() + + contact = gajim.contacts.get_contact_with_highest_priority(account, jid) + if not contact: + return + if contact.groups == [_('Groupchats')]: + self.remove_contact(contact, account) + gajim.contacts.remove_contact(account, contact) + self.draw_group(_('Groupchats'), account) + self.draw_account(account) + def on_send_single_message_menuitem_activate(self, widget, account, contact = None): if contact is None: @@ -1886,9 +1984,6 @@ class RosterWindow: add_special_notification_menuitem = xml.get_widget( 'add_special_notification_menuitem') - add_special_notification_menuitem.hide() - add_special_notification_menuitem.set_no_show_all(True) - if not our_jid: # add a special img for rename menuitem path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps', @@ -1904,13 +1999,13 @@ class RosterWindow: information_menuitem = xml.get_widget('information_menuitem') history_menuitem = xml.get_widget('history_menuitem') - contacts = gajim.contacts.get_contact(account, jid) + contacts = gajim.contacts.get_contacts(account, jid) if len(contacts) > 1: # several resources sub_menu = gtk.Menu() start_chat_menuitem.set_submenu(sub_menu) iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') for c in contacts: # icon MUST be different instance for every item state_images = self.load_iconset(path) @@ -1945,7 +2040,7 @@ class RosterWindow: account) if _('Not in Roster') not in contact.groups: - #contact is in normal group + # contact is in normal group edit_groups_menuitem.set_no_show_all(False) assign_openpgp_key_menuitem.set_no_show_all(False) edit_groups_menuitem.connect('activate', self.on_edit_groups, [( @@ -2008,13 +2103,35 @@ class RosterWindow: send_file_menuitem = xml.get_widget('send_file_menuitem') assign_openpgp_key_menuitem = xml.get_widget( 'assign_openpgp_key_menuitem') + set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem') add_special_notification_menuitem = xml.get_widget( 'add_special_notification_menuitem') execute_command_menuitem = xml.get_widget( 'execute_command_menuitem') - add_special_notification_menuitem.hide() - add_special_notification_menuitem.set_no_show_all(True) + # send custom status icon + blocked = False + if jid in gajim.connections[account].blocked_contacts: + blocked = True + else: + groups = contact.groups + if contact.is_observer(): + groups = [_('Observers')] + elif not groups: + groups = [_('General')] + for group in groups: + if group in gajim.connections[account].blocked_groups: + blocked = True + break + if blocked: + send_custom_status_menuitem.set_image(self.load_icon('offline')) + send_custom_status_menuitem.set_sensitive(False) + elif gajim.interface.status_sent_to_users.has_key(account) and \ + jid in gajim.interface.status_sent_to_users[account]: + send_custom_status_menuitem.set_image( + self.load_icon(gajim.interface.status_sent_to_users[account][jid])) + else: + send_custom_status_menuitem.set_image(None) if not our_jid: # add a special img for rename menuitem @@ -2041,12 +2158,12 @@ class RosterWindow: information_menuitem = xml.get_widget('information_menuitem') history_menuitem = xml.get_widget('history_menuitem') - contacts = gajim.contacts.get_contact(account, jid) + contacts = gajim.contacts.get_contacts(account, jid) # Invite to invite_to_submenu = gtk.Menu() invite_menuitem.set_submenu(invite_to_submenu) - invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New group chat')) + invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat')) icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU) invite_to_new_room_menuitem.set_image(icon) contact_transport = gajim.get_transport_name_from_jid(contact.jid) @@ -2056,7 +2173,8 @@ class RosterWindow: invite_to_submenu.append(invite_to_new_room_menuitem) rooms = [] # a list of (room_jid, account) tuple for gc_control in gajim.interface.msg_win_mgr.get_controls( - message_control.TYPE_GC): + message_control.TYPE_GC) + \ + gajim.interface.minimized_controls[account].values(): acct = gc_control.account room_jid = gc_control.room_jid if gajim.gc_connected[acct].has_key(room_jid) and \ @@ -2071,7 +2189,7 @@ class RosterWindow: status_menuitems = gtk.Menu() send_custom_status_menuitem.set_submenu(status_menuitems) iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']: # icon MUST be different instance for every item state_images = self.load_iconset(path) @@ -2083,14 +2201,14 @@ class RosterWindow: status_menuitems.append(status_menuitem) if len(contacts) > 1: # several resources def resources_submenu(action, room_jid = None, room_account = None): - ''' Build a submenu with contact's resources. + ''' Build a submenu with contact's resources. room_jid and room_account are for action self.on_invite_to_room ''' sub_menu = gtk.Menu() iconset = gajim.config.get('iconset') if not iconset: iconset = gajim.config.DEFAULT_ICONSET - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') for c in contacts: # icon MUST be different instance for every item state_images = self.load_iconset(path) @@ -2101,7 +2219,7 @@ class RosterWindow: item.set_image(icon) sub_menu.append(item) if action == self.on_invite_to_room: - item.connect('activate', action, [(c, account)], + item.connect('activate', action, [(c, account)], room_jid, room_account, c.resource) elif action == self.on_invite_to_new_room: item.connect('activate', action, [(c, account)], c.resource) @@ -2117,7 +2235,7 @@ class RosterWindow: self.on_invite_to_new_room)) for (room_jid, room_account) in rooms: menuitem = gtk.MenuItem(room_jid.split('@')[0]) - menuitem.set_submenu(resources_submenu(self.on_invite_to_room, + menuitem.set_submenu(resources_submenu(self.on_invite_to_room, room_jid, room_account)) invite_to_submenu.append(menuitem) @@ -2137,16 +2255,16 @@ class RosterWindow: our_jid_other_resource = None if our_jid: # It's another resource of us, be sure to send invite to her - our_jid_other_resource = contact.resource + our_jid_other_resource = contact.resource # Else this var is useless but harmless in next connect calls - - invite_to_new_room_menuitem.connect('activate', - self.on_invite_to_new_room, [(contact, account)], + + invite_to_new_room_menuitem.connect('activate', + self.on_invite_to_new_room, [(contact, account)], our_jid_other_resource) for (room_jid, room_account) in rooms: menuitem = gtk.MenuItem(room_jid.split('@')[0]) menuitem.connect('activate', self.on_invite_to_room, - [(contact, account)], room_jid, room_account, + [(contact, account)], room_jid, room_account, our_jid_other_resource) invite_to_submenu.append(menuitem) @@ -2169,7 +2287,7 @@ class RosterWindow: account) if _('Not in Roster') not in contact.groups: - #contact is in normal group + # contact is in normal group edit_groups_menuitem.set_no_show_all(False) assign_openpgp_key_menuitem.set_no_show_all(False) add_to_roster_menuitem.hide() @@ -2214,13 +2332,13 @@ class RosterWindow: add_to_roster_menuitem.connect('activate', self.on_add_to_roster, contact, account) + set_custom_avatar_menuitem.connect('activate', + self.on_set_custom_avatar_activate, contact, account) # Remove many items when it's self contact row if our_jid: - for menuitem in (rename_menuitem, edit_groups_menuitem, - above_subscription_separator, subscription_menuitem, - remove_from_roster_menuitem): - menuitem.set_no_show_all(True) - menuitem.hide() + menuitem = xml.get_widget('manage_contact') + menuitem.set_no_show_all(True) + menuitem.hide() # Unsensitive many items when account is offline if gajim.connections[account].connected < 2: @@ -2241,11 +2359,10 @@ class RosterWindow: block_menuitem.connect('activate', self.on_block, iter, None) unblock_menuitem.hide() else: - block_menuitem.set_no_show_all(True) unblock_menuitem.set_no_show_all(True) - block_menuitem.hide() + block_menuitem.set_sensitive(False) unblock_menuitem.hide() - + event_button = gtkgui_helpers.get_possible_button_event(event) roster_contact_context_menu.attach_to_widget(self.tree, None) @@ -2256,7 +2373,7 @@ class RosterWindow: event.time) def on_invite_to_new_room(self, widget, list_, resource = None): - ''' resource parameter MUST NOT be used if more than one contact in + ''' resource parameter MUST NOT be used if more than one contact in list ''' account_list = [] jid_list = [] @@ -2285,16 +2402,16 @@ class RosterWindow: continue break - def on_invite_to_room(self, widget, list_, room_jid, room_account, + def on_invite_to_room(self, widget, list_, room_jid, room_account, resource = None): - ''' resource parameter MUST NOT be used if more than one contact in + ''' resource parameter MUST NOT be used if more than one contact in list ''' for (contact, acct) in list_: contact_jid = contact.jid if resource: # we MUST have one contact only in list_ contact_jid += '/' + resource gajim.connections[room_account].send_invite(room_jid, contact_jid) - + def make_multiple_contact_menu(self, event, iters): '''Make group's popup menu''' @@ -2324,16 +2441,27 @@ class RosterWindow: list_.append((contact, account)) menu = gtk.Menu() - - remove_item = gtk.ImageMenuItem(_('_Remove from Roster')) - icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) - remove_item.set_image(icon) - menu.append(remove_item) - remove_item.connect('activate', self.on_req_usub, list_) - + account = None + for (contact, current_account) in list_: + # check that we use the same account for every sender + if account is not None and account != current_account: + account = None + break + account = current_account + if account is not None: + send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage')) + icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU) + send_group_message_item.set_image(icon) + menu.append(send_group_message_item) + send_group_message_item.connect('activate', + self.on_send_single_message_menuitem_activate, account, list_) + + # Invite to Groupchat invite_item = gtk.ImageMenuItem(_('In_vite to')) - icon = gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_MENU) - invite_item.set_image(icon) + muc_icon = self.load_icon('muc_active') + if muc_icon: + invite_item.set_image(muc_icon) + if contacts_transport == False: # they are not all from the same transport invite_item.set_sensitive(False) @@ -2354,7 +2482,8 @@ class RosterWindow: sub_menu.append(menuitem) rooms = [] # a list of (room_jid, account) tuple for gc_control in gajim.interface.msg_win_mgr.get_controls( - message_control.TYPE_GC): + message_control.TYPE_GC) + \ + gajim.interface.minimized_controls[account].values(): account = gc_control.account room_jid = gc_control.room_jid if gajim.gc_connected[account].has_key(room_jid) and \ @@ -2369,40 +2498,47 @@ class RosterWindow: menuitem.connect('activate', self.on_invite_to_room, list_, room_jid, account) sub_menu.append(menuitem) - + invite_item.set_submenu(sub_menu) menu.append(invite_item) - edit_groups_item = gtk.MenuItem(_('Edit _Groups')) + item = gtk.SeparatorMenuItem() # separator + menu.append(item) + + # Edit Groups + edit_groups_item = gtk.ImageMenuItem(_('Edit _Groups')) + icon = gtk.image_new_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_MENU) + edit_groups_item.set_image(icon) menu.append(edit_groups_item) edit_groups_item.connect('activate', self.on_edit_groups, list_) - - account = None - for (contact, current_account) in list_: - # check that we use the same account for every sender - if account is not None and account != current_account: - account = None - break - account = current_account - if account is not None: - send_group_message_item = gtk.MenuItem(_('Send Group M_essage')) - menu.append(send_group_message_item) - send_group_message_item.connect('activate', - self.on_send_single_message_menuitem_activate, account, list_) - if is_blocked: + item = gtk.SeparatorMenuItem() # separator + menu.append(item) + + # Block + if is_blocked and gajim.connections[account].privacy_rules_supported: unblock_menuitem = gtk.ImageMenuItem(_('_Unblock')) - icon = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) + icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) unblock_menuitem.set_image(icon) unblock_menuitem.connect('activate', self.on_unblock, None, list_) menu.append(unblock_menuitem) else: block_menuitem = gtk.ImageMenuItem(_('_Block')) - icon = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) + icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) block_menuitem.set_image(icon) block_menuitem.connect('activate', self.on_block, None, list_) menu.append(block_menuitem) - # unsensitive if one account is not connected + + if not gajim.connections[account].privacy_rules_supported: + block_menuitem.set_sensitive(False) + + # Remove + remove_item = gtk.ImageMenuItem(_('_Remove from Roster')) + icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) + remove_item.set_image(icon) + menu.append(remove_item) + remove_item.connect('activate', self.on_req_usub, list_) + # unsensitive remove if one account is not connected if one_account_offline: remove_item.set_sensitive(False) @@ -2419,32 +2555,53 @@ class RosterWindow: path = model.get_path(iter) jid = model[iter][C_JID].decode('utf-8') account = model[iter][C_ACCOUNT].decode('utf-8') - + contact = gajim.contacts.get_contact_with_highest_priority(account, jid) menu = gtk.Menu() - maximize_menuitem = gtk.ImageMenuItem(_('_Maximize')) - icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU) - maximize_menuitem.set_image(icon) - maximize_menuitem.connect('activate', self.on_groupchat_maximized, \ - jid, account) + if jid in gajim.interface.minimized_controls[account]: + maximize_menuitem = gtk.ImageMenuItem(_('_Maximize')) + icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU) + maximize_menuitem.set_image(icon) + maximize_menuitem.connect('activate', self.on_groupchat_maximized, \ + jid, account) + menu.append(maximize_menuitem) + + history_menuitem = gtk.ImageMenuItem(_('_History')) + history_icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, \ + gtk.ICON_SIZE_MENU) + history_menuitem.set_image(history_icon) + history_menuitem .connect('activate', self.on_history, \ + contact, account) + menu.append(history_menuitem) + + item = gtk.SeparatorMenuItem() # separator + menu.append(item) + + disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect')) + disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \ + gtk.ICON_SIZE_MENU) + disconnect_menuitem.set_image(disconnect_icon) + disconnect_menuitem .connect('activate', self.on_disconnect, jid, account) + menu.append(disconnect_menuitem) - menu.append(maximize_menuitem) - event_button = gtkgui_helpers.get_possible_button_event(event) - + menu.attach_to_widget(self.tree, None) menu.connect('selection-done', gtkgui_helpers.destroy_widget) menu.show_all() menu.popup(None, None, None, event_button, event.time) + def on_all_groupchat_maximized(self, widget, group_list): + for (contact, account) in group_list: + self.on_groupchat_maximized(widget, contact.jid, account) + + def on_groupchat_maximized(self, widget, jid, account): - '''When a groupshat is maximised''' - if not gajim.interface.minimized_controls.has_key(account): - return + '''When a groupchat is maximised''' if not gajim.interface.minimized_controls[account].has_key(jid): return - - + + ctrl = gajim.interface.minimized_controls[account][jid] mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account) if not mw: @@ -2454,12 +2611,16 @@ class RosterWindow: mw.new_tab(ctrl) mw.set_active_tab(jid, account) mw.window.present() + del gajim.interface.minimized_controls[account][jid] contact = gajim.contacts.get_contact_with_highest_priority(account, jid) - self.remove_contact(contact, account) - gajim.contacts.remove_contact(account, contact) - self.draw_group(_('Groupchats'), account) - del gajim.interface.minimized_controls[account][jid] + if not contact: + return + if contact.groups == [_('Groupchats')]: + self.remove_contact(contact, account) + gajim.contacts.remove_contact(account, contact) + self.draw_group(_('Groupchats'), account) + self.draw_account(account) def make_group_menu(self, event, iter): '''Make group's popup menu''' @@ -2468,41 +2629,6 @@ class RosterWindow: group = model[iter][C_JID].decode('utf-8') account = model[iter][C_ACCOUNT].decode('utf-8') - menu = gtk.Menu() - if not group in helpers.special_groups + (_('General'),): - - rename_item = gtk.ImageMenuItem(_('Re_name')) - # add a special img for rename menuitem - path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps', - 'kbd_input.png') - img = gtk.Image() - img.set_from_file(path_to_kbd_input_img) - rename_item.set_image(img) - menu.append(rename_item) - rename_item.connect('activate', self.on_rename, iter, path) - - # Remove group - remove_item = gtk.ImageMenuItem(_('_Remove from Roster')) - icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) - remove_item.set_image(icon) - menu.append(remove_item) - remove_item.connect('activate', self.on_remove_group_item_activated, - group, account) - - # unsensitive if account is not connected - if gajim.connections[account].connected < 2: - rename_item.set_sensitive(False) - send_group_message_item = gtk.MenuItem(_('Send Group M_essage')) - - send_group_message_submenu = gtk.Menu() - send_group_message_item.set_submenu(send_group_message_submenu) - menu.append(send_group_message_item) - - group_message_to_all_item = gtk.MenuItem(_('To all users')) - send_group_message_submenu.append(group_message_to_all_item) - - group_message_to_all_online_item = gtk.MenuItem(_('To all online users')) - send_group_message_submenu.append(group_message_to_all_online_item) list_ = [] # list of (jid, account) tuples list_online = [] # list of (jid, account) tuples @@ -2515,52 +2641,114 @@ class RosterWindow: if contact.show not in ('offline', 'error'): list_online.append((contact, account)) list_.append((contact, account)) + menu = gtk.Menu() - group_message_to_all_online_item.connect('activate', - self.on_send_single_message_menuitem_activate, account, list_online) - group_message_to_all_item.connect('activate', - self.on_send_single_message_menuitem_activate, account, list_) - - send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status')) - # add a special img for this menuitem - icon = gtk.image_new_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_MENU) - send_custom_status_menuitem.set_image(icon) - status_menuitems = gtk.Menu() - send_custom_status_menuitem.set_submenu(status_menuitems) - iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') - for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']: - # icon MUST be different instance for every item - state_images = self.load_iconset(path) - status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s)) - status_menuitem.connect('activate', self.on_send_custom_status, list_, - s) - icon = state_images[s] - status_menuitem.set_image(icon) - status_menuitems.append(status_menuitem) - menu.append(send_custom_status_menuitem) - is_blocked = False - if self.regroup: - for g_account in gajim.connections: - if group in gajim.connections[g_account].blocked_groups: - is_blocked = True + # Make special context menu if group is Groupchats + if group == _('Groupchats'): + maximize_menuitem = gtk.ImageMenuItem(_('_Maximize All')) + icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU) + maximize_menuitem.set_image(icon) + maximize_menuitem.connect('activate', self.on_all_groupchat_maximized, \ + list_) + menu.append(maximize_menuitem) else: + # Send Group Message + send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage')) + icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU) + send_group_message_item.set_image(icon) + + send_group_message_submenu = gtk.Menu() + send_group_message_item.set_submenu(send_group_message_submenu) + menu.append(send_group_message_item) + + group_message_to_all_item = gtk.MenuItem(_('To all users')) + send_group_message_submenu.append(group_message_to_all_item) + + group_message_to_all_online_item = gtk.MenuItem(_('To all online users')) + send_group_message_submenu.append(group_message_to_all_online_item) + + group_message_to_all_online_item.connect('activate', + self.on_send_single_message_menuitem_activate, account, list_online) + group_message_to_all_item.connect('activate', + self.on_send_single_message_menuitem_activate, account, list_) + + # Send Custom Status + send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status')) + # add a special img for this menuitem if group in gajim.connections[account].blocked_groups: - is_blocked = True - - if group not in helpers.special_groups + (_('General'),): - if is_blocked: + send_custom_status_menuitem.set_image(self.load_icon('offline')) + send_custom_status_menuitem.set_sensitive(False) + elif gajim.interface.status_sent_to_groups.has_key(account) and \ + group in gajim.interface.status_sent_to_groups[account]: + send_custom_status_menuitem.set_image(self.load_icon( + gajim.interface.status_sent_to_groups[account][group])) + else: + send_custom_status_menuitem.set_image(None) + status_menuitems = gtk.Menu() + send_custom_status_menuitem.set_submenu(status_menuitems) + iconset = gajim.config.get('iconset') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') + for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']: + # icon MUST be different instance for every item + state_images = self.load_iconset(path) + status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s)) + status_menuitem.connect('activate', self.on_send_custom_status, list_, + s, group) + icon = state_images[s] + status_menuitem.set_image(icon) + status_menuitems.append(status_menuitem) + menu.append(send_custom_status_menuitem) + + if not group in helpers.special_groups + (_('General'),): + item = gtk.SeparatorMenuItem() # separator + menu.append(item) + + # Rename + rename_item = gtk.ImageMenuItem(_('Re_name')) + # add a special img for rename menuitem + path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps', + 'kbd_input.png') + img = gtk.Image() + img.set_from_file(path_to_kbd_input_img) + rename_item.set_image(img) + menu.append(rename_item) + rename_item.connect('activate', self.on_rename, iter, path) + + # Block group + is_blocked = False + if self.regroup: + for g_account in gajim.connections: + if group in gajim.connections[g_account].blocked_groups: + is_blocked = True + else: + if group in gajim.connections[account].blocked_groups: + is_blocked = True + + if is_blocked and gajim.connections[account].privacy_rules_supported: unblock_menuitem = gtk.ImageMenuItem(_('_Unblock')) - icon = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) + icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) unblock_menuitem.set_image(icon) unblock_menuitem.connect('activate', self.on_unblock, iter, list_) menu.append(unblock_menuitem) else: block_menuitem = gtk.ImageMenuItem(_('_Block')) - icon = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) + icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) block_menuitem.set_image(icon) block_menuitem.connect('activate', self.on_block, iter, list_) menu.append(block_menuitem) + if not gajim.connections[account].privacy_rules_supported: + block_menuitem.set_sensitive(False) + + # Remove group + remove_item = gtk.ImageMenuItem(_('_Remove from Roster')) + icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) + remove_item.set_image(icon) + menu.append(remove_item) + remove_item.connect('activate', self.on_remove_group_item_activated, + group, account) + # unsensitive if account is not connected + if gajim.connections[account].connected < 2: + rename_item.set_sensitive(False) event_button = gtkgui_helpers.get_possible_button_event(event) @@ -2578,6 +2766,7 @@ class RosterWindow: contact = gajim.contacts.get_contact_with_highest_priority(account, jid) menu = gtk.Menu() + # Log on item = gtk.ImageMenuItem(_('_Log on')) icon = gtk.image_new_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU) item.set_image(icon) @@ -2588,6 +2777,7 @@ class RosterWindow: item.set_sensitive(False) item.connect('activate', self.on_agent_logging, jid, None, account) + # Log off item = gtk.ImageMenuItem(_('Log _off')) icon = gtk.image_new_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) item.set_image(icon) @@ -2601,14 +2791,7 @@ class RosterWindow: item = gtk.SeparatorMenuItem() # separator menu.append(item) - item = gtk.ImageMenuItem(_('_Edit')) - icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) - item.set_image(icon) - menu.append(item) - item.connect('activate', self.on_edit_agent, contact, account) - if gajim.account_is_disconnected(account): - item.set_sensitive(False) - + # Execute Command item = gtk.ImageMenuItem(_('Execute Command...')) icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) item.set_image(icon) @@ -2618,6 +2801,16 @@ class RosterWindow: if gajim.account_is_disconnected(account): item.set_sensitive(False) + # Edit + item = gtk.ImageMenuItem(_('_Edit')) + icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU) + item.set_image(icon) + menu.append(item) + item.connect('activate', self.on_edit_agent, contact, account) + if gajim.account_is_disconnected(account): + item.set_sensitive(False) + + # Rename item = gtk.ImageMenuItem(_('_Rename')) # add a special img for rename menuitem path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps', @@ -2629,7 +2822,11 @@ class RosterWindow: item.connect('activate', self.on_rename, iter, path) if gajim.account_is_disconnected(account): item.set_sensitive(False) + + item = gtk.SeparatorMenuItem() # sepator + menu.append(item) + # Remove item = gtk.ImageMenuItem(_('_Remove from Roster')) icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) item.set_image(icon) @@ -2638,6 +2835,13 @@ class RosterWindow: if gajim.account_is_disconnected(account): item.set_sensitive(False) + information_menuitem = gtk.ImageMenuItem(_('_Information')) + icon = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU) + information_menuitem.set_image(icon) + menu.append(information_menuitem) + information_menuitem.connect('activate', self.on_info, contact, account) + + event_button = gtkgui_helpers.get_possible_button_event(event) menu.attach_to_widget(self.tree, None) @@ -2646,25 +2850,23 @@ class RosterWindow: menu.popup(None, None, None, event_button, event.time) def on_edit_account(self, widget, account): - if gajim.interface.instances[account].has_key('account_modification'): - gajim.interface.instances[account]['account_modification'].\ - window.present() + if gajim.interface.instances.has_key('accounts'): + gajim.interface.instances['accounts'].window.present() else: - gajim.interface.instances[account]['account_modification'] = \ - config.AccountModificationWindow(account) + gajim.interface.instances['accounts'] = config.AccountsWindow() + gajim.interface.instances['accounts'].select_account(account) def on_zeroconf_properties(self, widget, account): - if gajim.interface.instances.has_key('zeroconf_properties'): - gajim.interface.instances['zeroconf_properties'].\ - window.present() + if gajim.interface.instances.has_key('accounts'): + gajim.interface.instances['accounts'].window.present() else: - gajim.interface.instances['zeroconf_properties'] = \ - config.ZeroconfPropertiesWindow() + gajim.interface.instances['accounts'] = config.AccountsWindow() + gajim.interface.instances['accounts'].select_account(account) def on_open_gmail_inbox(self, widget, account): - url = 'http://mail.google.com/mail?account_id=%s' % urllib.quote( - gajim.config.get_per('accounts', account, 'name')) - helpers.launch_browser_mailer('url', url) + url = gajim.connections[account].gmail_url + if url: + helpers.launch_browser_mailer('url', url) def on_change_activity_activate(self, widget, account): dlg = dialogs.ChangeActivityDialog(account) @@ -2683,7 +2885,7 @@ class RosterWindow: # we have to create our own set of icons for the menu # using self.jabber_status_images is poopoo iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') state_images = self.load_iconset(path) if not gajim.config.get_per('accounts', account, 'is_zeroconf'): @@ -2696,7 +2898,6 @@ class RosterWindow: if muc_icon: join_group_chat_menuitem.set_image(muc_icon) open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem') - new_message_menuitem = xml.get_widget('new_message_menuitem') add_contact_menuitem = xml.get_widget('add_contact_menuitem') service_discovery_menuitem = xml.get_widget( 'service_discovery_menuitem') @@ -2727,6 +2928,9 @@ class RosterWindow: if gajim.connections[account].connected < 2: item.set_sensitive(False) + item = gtk.SeparatorMenuItem() + sub_menu.append(item) + uf_show = helpers.get_uf_show('offline', use_mnemonic = True) item = gtk.ImageMenuItem(uf_show) icon = state_images['offline'] @@ -2748,8 +2952,7 @@ class RosterWindow: pep_menuitem.set_no_show_all(True) pep_menuitem.hide() - if gajim.config.get_per('accounts', account, 'hostname') not in \ - gajim.gmail_domains: + if not gajim.connections[account].gmail_url: open_gmail_inbox_menuitem.set_no_show_all(True) open_gmail_inbox_menuitem.hide() else: @@ -2766,17 +2969,15 @@ class RosterWindow: contact = gajim.contacts.create_contact(jid = hostname) # Fake contact execute_command_menuitem.connect('activate', self.on_execute_command, contact, account) - + gc_sub_menu = gtk.Menu() # gc is always a submenu join_group_chat_menuitem.set_submenu(gc_sub_menu) self.add_bookmarks_list(gc_sub_menu, account) - new_message_menuitem.connect('activate', - self.on_new_message_menuitem_activate, account) # make some items insensitive if account is offline if gajim.connections[account].connected < 2: for widget in [add_contact_menuitem, service_discovery_menuitem, - join_group_chat_menuitem, new_message_menuitem, + join_group_chat_menuitem, execute_command_menuitem, pep_menuitem]: widget.set_sensitive(False) else: @@ -2784,7 +2985,6 @@ class RosterWindow: account_context_menu = xml.get_widget('zeroconf_context_menu') status_menuitem = xml.get_widget('status_menuitem') - new_message_menuitem = xml.get_widget('new_message_menuitem') zeroconf_properties_menuitem = xml.get_widget( 'zeroconf_properties_menuitem') sub_menu = gtk.Menu() @@ -2845,7 +3045,7 @@ class RosterWindow: else: menu = gtk.Menu() iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') accounts = [] # Put accounts in a list to sort them for account in gajim.connections: accounts.append(account) @@ -3061,7 +3261,7 @@ class RosterWindow: self.tree.collapse_row(path) else: self.tree.expand_row(path, False) - + elif type_ == 'contact' and x < 27: account = model[path][C_ACCOUNT].decode('utf-8') jid = model[path][C_JID].decode('utf-8') @@ -3085,7 +3285,7 @@ class RosterWindow: remove_auth = False for (contact, account) in list_: gajim.connections[account].unsubscribe(contact.jid, remove_auth) - for c in gajim.contacts.get_contact(account, contact.jid): + for c in gajim.contacts.get_contacts(account, contact.jid): self.remove_contact(c, account) gajim.contacts.remove_jid(account, contact.jid) # redraw group rows for contact numbers @@ -3244,8 +3444,10 @@ class RosterWindow: gajim.SHOW_LIST.index('invisible') gajim.connections[account].change_status(status, txt, auto) - if not gajim.interface.minimized_controls.has_key(account): - gajim.interface.minimized_controls[account] = {} + if gajim.interface.status_sent_to_users.has_key(account): + gajim.interface.status_sent_to_users[account] = {} + if gajim.interface.status_sent_to_groups.has_key(account): + gajim.interface.status_sent_to_groups[account] = {} for gc_control in gajim.interface.msg_win_mgr.get_controls( message_control.TYPE_GC) + \ gajim.interface.minimized_controls[account].values(): @@ -3254,11 +3456,11 @@ class RosterWindow: gajim.connections[account].send_gc_status(gc_control.nick, gc_control.room_jid, status, txt) else: - # for some reason, we are not connected to the room even if + # for some reason, we are not connected to the room even if # tab is opened, send initial join_gc() - gajim.connections[account].join_gc(gc_control.nick, + gajim.connections[account].join_gc(gc_control.nick, gc_control.room_jid, None) - if was_invisible: + if was_invisible and status != 'offline': # We come back from invisible, join bookmarks for bm in gajim.connections[account].bookmarks: room_jid = bm['jid'] @@ -3266,14 +3468,14 @@ class RosterWindow: gajim.gc_connected[account][room_jid]: continue self.join_gc_room(account, room_jid, bm['nick'], bm['password'], - minimize = gajim.config.get('minimize_autojoined_rooms')) + minimize = bm['minimize']) def get_status_message(self, show): if show in gajim.config.get_per('defaultstatusmsg'): if gajim.config.get_per('defaultstatusmsg', show, 'enabled'): return gajim.config.get_per('defaultstatusmsg', show, 'message') if (show == 'online' and not gajim.config.get('ask_online_status')) or \ - (show in ('offline', 'invisible') + (show in ('offline', 'invisible') and not gajim.config.get('ask_offline_status')): return '' dlg = dialogs.ChangeStatusMessageDialog(show) @@ -3306,13 +3508,22 @@ class RosterWindow: else: change(None, account, status) - def on_send_custom_status(self, widget, contact_list, show): + def on_send_custom_status(self, widget, contact_list, show, group=None): '''send custom status''' dlg = dialogs.ChangeStatusMessageDialog(show) message = dlg.run() if message is not None: # None if user pressed Cancel for (contact, account) in contact_list: - self.send_status(account, show, message, to = contact.jid) + accounts = [] + if group and account not in accounts: + if not gajim.interface.status_sent_to_groups.has_key(account): + gajim.interface.status_sent_to_groups[account] = {} + gajim.interface.status_sent_to_groups[account][group] = show + accounts.append(group) + self.send_status(account, show, message, to = contact.jid) + if not gajim.interface.status_sent_to_users.has_key(account): + gajim.interface.status_sent_to_users[account] = {} + gajim.interface.status_sent_to_users[account][contact.jid] = show def on_status_combobox_changed(self, widget): '''When we change our status via the combobox''' @@ -3418,21 +3629,27 @@ class RosterWindow: listener.disconnect(self._music_track_changed_signal) self._music_track_changed_signal = None self._music_track_changed(None, None) - + def _change_awn_icon_status(self, status): if not dbus_support.supported: # do nothing if user doesn't have D-Bus bindings return + bus = dbus.SessionBus() + try: + if not 'com.google.code.Awn' in bus.list_names(): + # Awn is not installed + return + except: + pass iconset = gajim.config.get('iconset') - prefix = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '32x32') + prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32') if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'): status = status + '.png' elif status == 'online': prefix = os.path.join(gajim.DATA_DIR, 'pixmaps') status = 'gajim.png' path = os.path.join(prefix, status) - try: - bus = dbus.SessionBus() + try: obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn') awn = dbus.Interface(obj, 'com.google.code.Awn') awn.SetTaskIconByName('Gajim', os.path.abspath(path)) @@ -3516,7 +3733,7 @@ class RosterWindow: if gajim.con_types.has_key(account): gajim.con_types[account] = None for jid in gajim.contacts.get_jid_list(account): - lcontact = gajim.contacts.get_contact(account, jid) + lcontact = gajim.contacts.get_contacts(account, jid) lcontact_copy = [] for contact in lcontact: lcontact_copy.append(contact) @@ -3569,7 +3786,7 @@ class RosterWindow: if not contact: added_to_roster = True contact = self.add_to_not_in_the_roster(account, jid, - resource = resource) + resource = resource) if not gajim.interface.msg_win_mgr.has_window(fjid, account): self.new_chat(contact, account, resource = resource) @@ -3608,7 +3825,7 @@ class RosterWindow: if not contact: # If there is another resource, it may be a message from an invisible # resource - lcontact = gajim.contacts.get_contacts_from_jid(account, jid) + lcontact = gajim.contacts.get_contacts(account, jid) if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \ lcontact[0].show != 'offline')) and jid.find('@') > 0: contact = gajim.contacts.copy_contact(highest_contact) @@ -3682,8 +3899,8 @@ class RosterWindow: if path and not self.dragging and gajim.config.get( 'scroll_roster_to_last_message'): # we curently see contact in our roster OR he - # is not in the roster at all. - # show and select his line in roster + # is not in the roster at all. + # show and select his line in roster # do not change selection while DND'ing self.tree.expand_row(path[0:1], False) self.tree.expand_row(path[0:2], False) @@ -3730,9 +3947,6 @@ class RosterWindow: except GajimGeneralException: pass - def on_new_message_menuitem_activate(self, widget, account): - dialogs.SingleMessageWindow(account, action = 'send') - def on_new_chat_menuitem_activate(self, widget, account): dialogs.NewChatDialog(account) @@ -3743,6 +3957,9 @@ class RosterWindow: helpers.launch_browser_mailer('url', 'http://trac.gajim.org/wiki/GajimFaq') + def on_features_menuitem_activate(self, widget): + features_window.FeaturesWindow() + def on_about_menuitem_activate(self, widget): dialogs.AboutDialog() @@ -3765,7 +3982,7 @@ class RosterWindow: def on_manage_bookmarks_menuitem_activate(self, widget): config.ManageBookmarksWindow() - + def on_profile_avatar_menuitem_activate(self, widget, account): gajim.interface.edit_own_details(account) @@ -3776,12 +3993,13 @@ class RosterWindow: self.close_all_from_dict(w) else: w.window.destroy() - + def close_all(self, account, force = False): '''close all the windows from an account if force is True, do not ask confirmation before closing chat/gc windows ''' - self.close_all_from_dict(gajim.interface.instances[account]) + if account in gajim.interface.instances: + self.close_all_from_dict(gajim.interface.instances[account]) for ctrl in gajim.interface.msg_win_mgr.get_controls(acct = account): ctrl.parent_win.remove_tab(ctrl, ctrl.parent_win.CLOSE_CLOSE_BUTTON, force = force) @@ -3805,7 +4023,7 @@ class RosterWindow: if message is None: # user pressed Cancel to change status message dialog message = '' - + for acct in accounts: if gajim.connections[acct].connected: self.quit_on_next_offline += 1 @@ -4012,14 +4230,13 @@ class RosterWindow: self.tree.collapse_row(path) else: self.tree.expand_row(path, False) - elif gajim.interface.minimized_controls.has_key(account) and \ - gajim.interface.minimized_controls[account].has_key(jid): + elif jid in gajim.interface.minimized_controls[account]: self.on_groupchat_maximized(None, jid, account) else: first_ev = gajim.events.get_first_event(account, jid) if not first_ev: # look in other resources - for c in gajim.contacts.get_contact(account, jid): + for c in gajim.contacts.get_contacts(account, jid): fjid = c.get_full_jid() first_ev = gajim.events.get_first_event(account, fjid) if first_ev: @@ -4131,22 +4348,22 @@ class RosterWindow: 'not in roster') else: list = ('connecting', 'online', 'chat', 'away', 'xa', 'dnd', - 'invisible', 'offline', 'error', 'requested', 'message', 'opened', + 'invisible', 'offline', 'error', 'requested', 'event', 'opened', 'closed', 'not in roster', 'muc_active', 'muc_inactive') if pixbuf2: list = ('connecting', 'online', 'chat', 'away', 'xa', 'dnd', - 'offline', 'error', 'requested', 'message', 'not in roster') + 'offline', 'error', 'requested', 'event', 'not in roster') return self._load_icon_list(list, path, pixbuf2) - + def load_icon(self, icon_name): '''load an icon from the iconset in 16x16''' iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16'+ '/') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16'+ '/') icon_list = self._load_icon_list([icon_name], path) return icon_list[icon_name] - + def _load_icon_list(self, icons_list, path, pixbuf2 = None): - '''load icons in icons_list from the given path, + '''load icons in icons_list from the given path, and add pixbuf2 on top left of each static images''' imgs = {} for icon in icons_list: @@ -4176,20 +4393,34 @@ class RosterWindow: '''initialise jabber_state_images dict''' iconset = gajim.config.get('iconset') if iconset: - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') if not os.path.exists(path): iconset = gajim.config.DEFAULT_ICONSET - else: + else: iconset = gajim.config.DEFAULT_ICONSET - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '32x32') + path = os.path.join(helpers.get_iconset_path(iconset), '32x32') self.jabber_state_images['32'] = self.load_iconset(path) - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') self.jabber_state_images['16'] = self.load_iconset(path) - pixo = gtk.gdk.pixbuf_new_from_file(os.path.join(path, 'opened.png')) + # try to find opened_meta.png file, else opened.png else nopixbuf merge + path_opened = os.path.join(path, 'opened_meta.png') + if not os.path.isfile(path_opened): + path_opened = os.path.join(path, 'opened.png') + if os.path.isfile(path_opened): + pixo = gtk.gdk.pixbuf_new_from_file(path_opened) + else: + pixo = None self.jabber_state_images['opened'] = self.load_iconset(path, pixo) - pixc = gtk.gdk.pixbuf_new_from_file(os.path.join(path, 'closed.png')) + # Same thing for closed + path_closed = os.path.join(path, 'opened_meta.png') + if not os.path.isfile(path_closed): + path_closed = os.path.join(path, 'closed.png') + if os.path.isfile(path_closed): + pixc = gtk.gdk.pixbuf_new_from_file(path_closed) + else: + pixc = None self.jabber_state_images['closed'] = self.load_iconset(path, pixc) if gajim.config.get('use_transports_iconsets'): @@ -4243,7 +4474,7 @@ class RosterWindow: redraw the treeview''' gajim.config.set('showoffline', not gajim.config.get('showoffline')) self.draw_roster() - + def set_renderer_color(self, renderer, style, set_background = True): '''set style for treeview cell, using PRELIGHT system color''' if set_background: @@ -4252,7 +4483,7 @@ class RosterWindow: else: fgcolor = self.tree.style.fg[style] renderer.set_property('foreground-gdk', fgcolor) - + def iconCellDataFunc(self, column, renderer, model, iter, data = None): '''When a row is added, set properties for icon renderer''' theme = gajim.config.get('roster_theme') @@ -4366,6 +4597,11 @@ class RosterWindow: padlock)''' theme = gajim.config.get('roster_theme') type_ = model[iter][C_TYPE] + # allocate space for the icon only if needed + if model[iter][C_SECPIXBUF]: + renderer.set_property('visible', True) + else: + renderer.set_property('visible', False) if type_ == 'account': color = gajim.config.get_per('themes', theme, 'accountbgcolor') if color: @@ -4432,6 +4668,10 @@ class RosterWindow: return 1 if name2 == _('Not in Roster'): return -1 + if name1 == _('Groupchats'): + return 1 + if name2 == _('Groupchats'): + return -1 account1 = model[iter1][C_ACCOUNT] account2 = model[iter2][C_ACCOUNT] if not account1 or not account2: @@ -4445,13 +4685,13 @@ class RosterWindow: jid1 = model[iter1][C_JID].decode('utf-8') jid2 = model[iter2][C_JID].decode('utf-8') if type1 == 'contact': - lcontact1 = gajim.contacts.get_contact(account1, jid1) + lcontact1 = gajim.contacts.get_contacts(account1, jid1) contact1 = gajim.contacts.get_first_contact_from_jid(account1, jid1) if not contact1: return 0 name1 = contact1.get_shown_name() if type2 == 'contact': - lcontact2 = gajim.contacts.get_contact(account2, jid2) + lcontact2 = gajim.contacts.get_contacts(account2, jid2) contact2 = gajim.contacts.get_first_contact_from_jid(account2, jid2) if not contact2: return 0 @@ -4476,12 +4716,6 @@ class RosterWindow: return -1 elif show1 > show2: return 1 - if show1 == 6 and show2 == 6: - # If both are offline, and one has a status message, it is above - if contact1.status and not contact2.status: - return -1 - elif contact2.status and not contact1.status: - return 1 # We compare names if name1.lower() < name2.lower(): return -1 @@ -4574,13 +4808,24 @@ class RosterWindow: if not confirm_metacontacts: # First time we see this window dlg.checkbutton.set_active(True) - def on_drop_in_group(self, widget, account, c_source, grp_dest, context, - etime, grp_source = None): + def on_drop_in_group(self, widget, account, c_source, grp_dest, is_big_brother, + context, etime, grp_source = None): if grp_source: self.remove_contact_from_group(account, c_source, grp_source) - # remove tag - gajim.contacts.remove_metacontact(account, c_source.jid) + if not is_big_brother: + # remove tag before readding + gajim.contacts.remove_metacontact(account, c_source.jid) self.add_contact_to_group(account, c_source, grp_dest) + if is_big_brother: + # add whole metacontact to new group + tag = gajim.contacts.get_metacontacts_tag(account, c_source.jid) + all_jid = gajim.contacts.get_metacontacts_jids(tag) + for _account in all_jid: + for _jid in all_jid[_account]: + _c = gajim.contacts.get_first_contact_from_jid(_account, _jid) + if grp_source: + self.remove_contact_from_group(_account, _c, grp_source) + self.add_contact_to_group(_account, _c, grp_dest) if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY): context.finish(True, True, etime) @@ -4604,36 +4849,36 @@ class RosterWindow: def drag_data_received_data(self, treeview, context, x, y, selection, info, etime): - model = treeview.get_model() - if not selection.data: - return - data = selection.data drop_info = treeview.get_dest_row_at_pos(x, y) if not drop_info: return + if not selection.data: + return # prevents tb when several entrys are dragged + model = treeview.get_model() + data = selection.data path_dest, position = drop_info + if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2 \ and path_dest[1] == 0: # dropped before the first group return + if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2: + # dropped before a group: we drop it in the previous group every time + path_dest = (path_dest[0], path_dest[1]-1) + # destination: the row something got dropped on iter_dest = model.get_iter(path_dest) type_dest = model[iter_dest][C_TYPE].decode('utf-8') jid_dest = model[iter_dest][C_JID].decode('utf-8') account_dest = model[iter_dest][C_ACCOUNT].decode('utf-8') + # drop on account row in merged mode, we cannot know the desired account if account_dest == 'all': - # drop on account row in merged mode: we can't know which account it is return - - # if account is not connected, do nothing + # nothing can be done, if destination account is offline if gajim.connections[account_dest].connected < 2: return - # drop on self contact row - if type_dest == 'self_contact': - return - + # A file got dropped on the roster if info == self.TARGET_TYPE_URI_LIST: - # User dropped a file on the roster if len(path_dest) < 3: return if type_dest != 'contact': @@ -4643,55 +4888,72 @@ class RosterWindow: uri = data.strip() uri_splitted = uri.split() # we may have more than one file dropped nb_uri = len(uri_splitted) - prim_text = 'Send file?' - sec_text = i18n.ngettext('Do you want to send that file to %s:', - 'Do you want to send those files to %s:', nb_uri) %\ - c_dest.get_shown_name() - for uri in uri_splitted: - path = helpers.get_file_path_from_dnd_dropped_uri(uri) - sec_text += '\n' + os.path.basename(path) - def _on_send_files(widget, account, jid, uri): + def _on_send_files(widget, account, jid, uris): dialog.destroy() c = gajim.contacts.get_contact_with_highest_priority(account, jid) - uri_splitted = uri.split() # we may have more than one file dropped - for uri in uri_splitted: + for uri in uris: path = helpers.get_file_path_from_dnd_dropped_uri(uri) if os.path.isfile(path): # is it file? gajim.interface.instances['file_transfers'].send_file( account, c, path) - + # Popup dialog to confirm sending + prim_text = 'Send file?' + sec_text = i18n.ngettext('Do you want to send that file to %s:', + 'Do you want to send those files to %s:', nb_uri) %\ + c_dest.get_shown_name() + for uri in uri_splitted: + path = helpers.get_file_path_from_dnd_dropped_uri(uri) + sec_text += '\n' + os.path.basename(path) dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text, - on_response_ok = (_on_send_files, account_dest, jid_dest, uri)) + on_response_ok = (_on_send_files, account_dest, jid_dest, + uri_splitted)) dialog.popup() return + + # a roster entry was dragged and dropped somewhere in the roster - if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'): - # drop on zeroconf account, no contact adds possible - return - - if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2: - # dropped before a group : we drop it in the previous group - path_dest = (path_dest[0], path_dest[1]-1) + # source: the row that was dragged path_source = treeview.get_selection().get_selected_rows()[1][0] iter_source = model.get_iter(path_source) type_source = model[iter_source][C_TYPE] account_source = model[iter_source][C_ACCOUNT].decode('utf-8') - if type_source != 'contact': # source is not a contact - return - if type_dest == 'account' and account_source == account_dest: + + # Only normal contacts can be dragged + if type_source != 'contact': return if gajim.config.get_per('accounts', account_source, 'is_zeroconf'): return + + # A contact was dropped + if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'): + # drop on zeroconf account, adding not possible + return + if type_dest == 'self_contact': + # drop on self contact row + return + if type_dest == 'account' and account_source == account_dest: + # drop on the account it was dragged from + return + if type_dest == 'groupchat': + # drop on a minimized groupchat + # TODO: Invite to groupchat + return + + # Get valid source group, jid and contact it = iter_source while model[it][C_TYPE] == 'contact': it = model.iter_parent(it) grp_source = model[it][C_JID].decode('utf-8') - if grp_source in helpers.special_groups: + if grp_source in helpers.special_groups and \ + grp_source not in ('Not in Roster', 'Observers'): + # a transport or a minimized groupchat was dragged + # we can add it to other accounts but not move it to another group, see below return jid_source = data.decode('utf-8') c_source = gajim.contacts.get_contact_with_highest_priority( account_source, jid_source) + # Get destination group grp_dest = None if type_dest == 'group': grp_dest = model[iter_dest][C_JID].decode('utf-8') @@ -4700,76 +4962,53 @@ class RosterWindow: while model[it][C_TYPE] != 'group': it = model.iter_parent(it) grp_dest = model[it][C_JID].decode('utf-8') + if grp_dest in helpers.special_groups: + return + if jid_source == jid_dest: + if grp_source == grp_dest and account_source == account_dest: + # Drop on self + return + + # contact drop somewhere in or on a foreign account if (type_dest == 'account' or not self.regroup) and \ - account_source != account_dest: - # add contact to this account in that group + account_source != account_dest: + # add to account in specified group dialogs.AddNewContactWindow(account = account_dest, jid = jid_source, user_nick = c_source.name, group = grp_dest) return + + # we may not add contacts from special_groups + if grp_source in helpers.special_groups : + return - # Get destination group - if type_dest == 'group': - if grp_dest in helpers.special_groups: - return - if context.action == gtk.gdk.ACTION_COPY: - self.on_drop_in_group(None, account_source, c_source, grp_dest, - context, etime) - return - self.on_drop_in_group(None, account_source, c_source, grp_dest, - context, etime, grp_source) - return - if grp_dest in helpers.special_groups: - return - if jid_source == jid_dest: - if grp_source == grp_dest and account_source == account_dest: - return - if grp_source == grp_dest: - # Add meta contact - #FIXME: doesn't work under windows: - # http://bugzilla.gnome.org/show_bug.cgi?id=329797 -# if context.action == gtk.gdk.ACTION_COPY: -# # Keep only MOVE -# return - c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest, - jid_dest) - is_big_brother = False + # Is the contact we drag a meta contact? + is_meta_contact = False + is_big_brother = False + tag = gajim.contacts.get_metacontacts_tag(account_source, jid_source) + if tag: + is_meta_contact = True if model.iter_has_child(iter_source): is_big_brother = True + + # Contact drop on group row or between two contacts + if type_dest == 'group' or position == gtk.TREE_VIEW_DROP_BEFORE or \ + position == gtk.TREE_VIEW_DROP_AFTER: + self.on_drop_in_group(None, account_source, c_source, grp_dest, + is_big_brother, context, etime, grp_source) + return + + # Contact drop on another contact, make meta contacts + if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \ + position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE: + c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest, + jid_dest) if not c_dest: # c_dest is None if jid_dest doesn't belong to account return self.on_drop_in_contact(treeview, account_source, c_source, account_dest, c_dest, is_big_brother, context, etime) return - # We upgrade only the first user because user2.groups is a pointer to - # user1.groups - if context.action == gtk.gdk.ACTION_COPY: - self.on_drop_in_group(None, account_source, c_source, grp_dest, - context, etime) - else: - menu = gtk.Menu() - item = gtk.MenuItem(_('Drop %s in group %s') % (c_source.name, - grp_dest)) - item.connect('activate', self.on_drop_in_group, account_source, - c_source, grp_dest, context, etime, grp_source) - menu.append(item) - c_dest = gajim.contacts.get_contact_with_highest_priority( - account_dest, jid_dest) - item = gtk.MenuItem(_('Make %s and %s metacontacts') % - (c_source.get_shown_name(), c_dest.get_shown_name())) - is_big_brother = False - if model.iter_has_child(iter_source): - is_big_brother = True - item.connect('activate', self.on_drop_in_contact, account_source, - c_source, account_dest, c_dest, is_big_brother, context, etime) - - menu.append(item) - - menu.attach_to_widget(self.tree, None) - menu.connect('selection-done', gtkgui_helpers.destroy_widget) - menu.show_all() - menu.popup(None, None, None, 1, etime) def show_title(self): change_title_allowed = gajim.config.get('change_roster_title') @@ -4987,6 +5226,7 @@ class RosterWindow: col.set_cell_data_func(render_image, self.iconCellDataFunc, None) render_text = gtk.CellRendererText() # contact or group or account name + render_text.set_property("ellipsize", pango.ELLIPSIZE_END) col.pack_start(render_text, expand = True) col.add_attribute(render_text, 'markup', C_NAME) # where we hold the name col.set_cell_data_func(render_text, self.nameCellDataFunc, None) @@ -5006,10 +5246,11 @@ class RosterWindow: self.tree.append_column(col) col.set_visible(False) self.tree.set_expander_column(col) - + #signals self.TARGET_TYPE_URI_LIST = 80 - TARGETS = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)] + TARGETS = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, + 0)] TARGETS2 = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0), ('text/uri-list', 0, self.TARGET_TYPE_URI_LIST)] self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, TARGETS, @@ -5046,3 +5287,7 @@ class RosterWindow: if len(gajim.connections) == 0: # if we have no account gajim.interface.instances['account_creation_wizard'] = \ config.AccountCreationWizardWindow() + if not gajim.ZEROCONF_ACC_NAME in gajim.config.get_per('accounts'): + # Create zeroconf in config file + zeroconf = common.zeroconf.connection_zeroconf.ConnectionZeroconf( + gajim.ZEROCONF_ACC_NAME) diff --git a/src/search_window.py b/src/search_window.py index 4c95eef8f..ce40eb282 100644 --- a/src/search_window.py +++ b/src/search_window.py @@ -19,6 +19,8 @@ from common import xmpp, gajim, dataforms import gtkgui_helpers import dialogs +import vcard +import config import dataforms_widget class SearchWindow: @@ -32,12 +34,10 @@ class SearchWindow: # retrieving widgets from xml self.xml = gtkgui_helpers.get_glade('search_window.glade') self.window = self.xml.get_widget('search_window') - for name in ('label', 'progressbar', 'search_vbox', 'search_button'): + for name in ('label', 'progressbar', 'search_vbox', 'search_button', + 'add_contact_button', 'information_button'): self.__dict__[name] = self.xml.get_widget(name) - self.data_form_widget = dataforms_widget.DataFormWidget() - self.table = None - # displaying the window self.xml.signal_autoconnect(self) self.window.show_all() @@ -45,10 +45,9 @@ class SearchWindow: self.pulse_id = gobject.timeout_add(80, self.pulse_callback) self.is_form = None - - # for non-dataform forms - self.entries = {} - self.info = {} + + # Is there a jid column in results ? if -1: no, else column number + self.jid_column = -1 def request_form(self): gajim.connections[self.account].request_search_fields(self.jid) @@ -74,15 +73,14 @@ class SearchWindow: self.data_form_widget.data_form.type = 'submit' gajim.connections[self.account].send_search_form(self.jid, self.data_form_widget.data_form, True) - self.search_vbox.remove(self.data_form_widget) else: - for name in self.entries.keys(): - self.infos[name] = self.entries[name].get_text().decode('utf-8') - if self.infos.has_key('instructions'): - del self.infos['instructions'] - gajim.connections[self.account].send_search_form(self.jid, self.infos, + infos = self.data_form_widget.get_infos() + if infos.has_key('instructions'): + del infos['instructions'] + gajim.connections[self.account].send_search_form(self.jid, infos, False) - self.search_vbox.remove(self.table) + + self.search_vbox.remove(self.data_form_widget) self.progressbar.show() self.label.set_text(_('Waiting for results')) @@ -90,58 +88,66 @@ class SearchWindow: self.pulse_id = gobject.timeout_add(80, self.pulse_callback) self.search_button.hide() + def on_add_contact_button_clicked(self, widget): + (model, iter) = self.result_treeview.get_selection().get_selected() + if not iter: + return + jid = model[iter][self.jid_column] + dialogs.AddNewContactWindow(self.account, jid) + + def on_information_button_clicked(self, widget): + (model, iter) = self.result_treeview.get_selection().get_selected() + if not iter: + return + jid = model[iter][self.jid_column] + if gajim.interface.instances[self.account]['infos'].has_key(jid): + gajim.interface.instances[self.account]['infos'][jid].window.present() + else: + contact = gajim.contacts.create_contact(jid = jid, name='', groups=[], + show='', status='', sub='', ask='', resource='', priority=0, + keyID='', our_chatstate=None, chatstate=None) + gajim.interface.instances[self.account]['infos'][jid] = \ + vcard.VcardWindow(contact, self.account) + def on_form_arrived(self, form, is_form): if self.pulse_id: gobject.source_remove(self.pulse_id) self.progressbar.hide() self.label.hide() - if not is_form: - self.is_form = False - self.infos = form - nbrow = 0 - if self.infos.has_key('instructions'): - self.label.set_text(self.infos['instructions']) + if is_form: + self.is_form = True + self.data_form_widget = dataforms_widget.DataFormWidget() + self.dataform = dataforms.ExtendForm(node = form) + self.data_form_widget.set_sensitive(True) + try: + self.data_form_widget.data_form = self.dataform + except dataforms.Error: + self.label.set_text(_('Error in received dataform')) self.label.show() - self.table = gtk.Table() - for name in self.infos.keys(): - if not name: - continue - if name == 'instructions': - continue - - nbrow = nbrow + 1 - self.table.resize(rows = nbrow, columns = 2) - label = gtk.Label(name.capitalize() + ':') - self.table.attach(label, 0, 1, nbrow - 1, nbrow, 0, 0, 0, 0) - entry = gtk.Entry() - entry.set_activates_default(True) - if self.infos[name]: - entry.set_text(self.infos[name]) - if name == 'password': - entry.set_visibility(False) - self.table.attach(entry, 1, 2, nbrow - 1, nbrow, 0, 0, 0, 0) - self.entries[name] = entry - self.table.show_all() - self.search_vbox.pack_start(self.table) - return - - self.dataform = dataforms.ExtendForm(node = form) - - self.data_form_widget.set_sensitive(True) - try: - self.data_form_widget.data_form = self.dataform - except dataforms.Error: - self.label.set_text(_('Error in received dataform')) - self.label.show() - return - self.is_form = True + return + if self.data_form_widget.title: + self.window.set_title('%s - Search - Gajim' % \ + self.data_form_widget.title) + else: + self.is_form = False + self.data_form_widget = config.FakeDataForm(form) + self.data_form_widget.show_all() self.search_vbox.pack_start(self.data_form_widget) - self.data_form_widget.show() - if self.data_form_widget.title: - self.window.set_title('%s - Search - Gajim' % \ - self.data_form_widget.title) + + def on_result_treeview_cursor_changed(self, treeview): + if self.jid_column == -1: + return + (model, iter) = treeview.get_selection().get_selected() + if not iter: + return + if model[iter][self.jid_column]: + self.add_contact_button.set_sensitive(True) + self.information_button.set_sensitive(True) + else: + self.add_contact_button.set_sensitive(False) + self.information_button.set_sensitive(False) def on_result_arrived(self, form, is_form): if self.pulse_id: @@ -157,8 +163,10 @@ class SearchWindow: # We suppose all items have the same fields sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - treeview = gtk.TreeView() - sw.add(treeview) + self.result_treeview = gtk.TreeView() + self.result_treeview.connect('cursor-changed', + self.on_result_treeview_cursor_changed) + sw.add(self.result_treeview) # Create model fieldtypes = [str]*len(form[0]) model = gtk.ListStore(*fieldtypes) @@ -168,13 +176,18 @@ class SearchWindow: # Create columns counter = 0 for field in form[0].keys(): - treeview.append_column( + self.result_treeview.append_column( gtk.TreeViewColumn(field, gtk.CellRendererText(), text = counter)) + if field == 'jid': + self.jid_column = counter counter += 1 - treeview.set_model(model) + self.result_treeview.set_model(model) sw.show_all() self.search_vbox.pack_start(sw) + if self.jid_column > -1: + self.add_contact_button.show() + self.information_button.show() return self.dataform = dataforms.ExtendForm(node = form) @@ -187,8 +200,23 @@ class SearchWindow: self.label.show() return + self.result_treeview = self.data_form_widget.records_treeview + selection = self.result_treeview.get_selection() + selection.set_mode(gtk.SELECTION_SINGLE) + self.result_treeview.connect('cursor-changed', + self.on_result_treeview_cursor_changed) + + counter = 0 + for field in self.dataform.items[0].fields: + if field.var == 'jid': + self.jid_column = counter + break + counter += 1 self.search_vbox.pack_start(self.data_form_widget) self.data_form_widget.show() + if self.jid_column > -1: + self.add_contact_button.show() + self.information_button.show() if self.data_form_widget.title: self.window.set_title('%s - Search - Gajim' % \ self.data_form_widget.title) diff --git a/src/statusicon.py b/src/statusicon.py index 1e9f0ae4e..6610e0087 100644 --- a/src/statusicon.py +++ b/src/statusicon.py @@ -1,6 +1,7 @@ ## statusicon.py ## ## Copyright (C) 2006 Nikos Kouremenos +## Copyright (C) 2007 Lukas Petrovicky ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License @@ -56,7 +57,7 @@ class StatusIcon(systray.Systray): text = helpers.get_notification_icon_tooltip_text() self.status_icon.set_tooltip(text) if gajim.events.get_nb_systray_events(): - state = 'message' # FIXME: this state should be called event, not message + state = 'event' self.status_icon.props.blinking = True else: state = self.status diff --git a/src/systray.py b/src/systray.py index 0ba839c85..84ae49877 100644 --- a/src/systray.py +++ b/src/systray.py @@ -6,6 +6,7 @@ ## Copyright (C) 2005 Dimitur Kirov ## Copyright (C) 2005-2006 Travis Shirk ## Copyright (C) 2005 Norman Rasmussen +## Copyright (C) 2007 Lukas Petrovicky ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -82,7 +83,7 @@ class Systray: if not gajim.interface.systray_enabled: return if gajim.events.get_nb_systray_events(): - state = 'message' + state = 'event' else: state = self.status image = gajim.interface.roster.jabber_state_images['16'][state] @@ -144,7 +145,7 @@ class Systray: # We need our own set of status icons, let's make 'em! iconset = gajim.config.get('iconset') - path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + path = os.path.join(helpers.get_iconset_path(iconset), '16x16') state_images = gajim.interface.roster.load_iconset(path) if state_images.has_key('muc_active'): @@ -214,16 +215,13 @@ class Systray: account_menu_for_single_message.append(item) # join gc - label = gtk.Label() - label.set_markup('' + account.upper() +'') - label.set_use_underline(False) - gc_item = gtk.MenuItem() - gc_item.add(label) - gc_item.connect('state-changed', - gtkgui_helpers.on_bm_header_changed_state) + gc_item = gtk.MenuItem(_('using account %s') % account, False) gc_sub_menu.append(gc_item) - gajim.interface.roster.add_bookmarks_list(gc_sub_menu, + gc_menuitem_menu = gtk.Menu() + gajim.interface.roster.add_bookmarks_list(gc_menuitem_menu, account) + gc_item.set_submenu(gc_menuitem_menu) + gc_sub_menu.show_all() elif connected_accounts == 1: # one account # one account connected, no need to show 'as jid' diff --git a/src/tooltips.py b/src/tooltips.py index 8e68ddfbb..f7aa83f75 100644 --- a/src/tooltips.py +++ b/src/tooltips.py @@ -4,6 +4,7 @@ ## Copyright (C) 2005-2006 Dimitur Kirov ## Copyright (C) 2005-2007 Nikos Kouremenos ## Copyright (C) 2005-2006 Yann Le Boulanger +## Copyright (C) 2007 Julien Pivotto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -161,13 +162,15 @@ class StatusTable: self.table = gtk.Table(4, 1) self.table.set_property('column-spacing', 2) - def add_text_row(self, text): + def add_text_row(self, text, col_inc = 0): + self.current_row += 1 self.text_label = gtk.Label() self.text_label.set_line_wrap(True) self.text_label.set_alignment(0, 0) self.text_label.set_selectable(False) self.text_label.set_markup(text) - self.table.attach(self.text_label, 1, 4, 1, 2) + self.table.attach(self.text_label, 1 + col_inc, 4, self.current_row, + self.current_row + 1) def get_status_info(self, resource, priority, show, status): str_status = resource + ' (' + unicode(priority) + ')' @@ -226,7 +229,7 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): iconset = gajim.config.get('iconset') if not iconset: iconset = 'dcraven' - file_path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16') + file_path = os.path.join(helpers.get_iconset_path(iconset), '16x16') for acct in accounts: message = acct['message'] # before reducing the chars we should assure we send unicode, else @@ -248,21 +251,19 @@ class NotificationAreaTooltip(BaseTooltip, StatusTable): self.add_status_row(file_path, acct['show'], gobject.markup_escape_text(acct['name']) , show_lock=show_lock) + for line in acct['event_lines']: + self.add_text_row(' ' + line, 2) def populate(self, data): self.create_window() self.create_table() - accounts = helpers.get_accounts_info() - if len(accounts) > 1: - self.table.resize(2, 1) - self.fill_table_with_accounts(accounts) + + accounts = helpers.get_notification_icon_tooltip_dict() + self.table.resize(2, 1) + self.fill_table_with_accounts(accounts) self.hbox = gtk.HBox() self.table.set_property('column-spacing', 1) - text = helpers.get_notification_icon_tooltip_text() - text = gobject.markup_escape_text(text) - - self.add_text_row(text) self.hbox.add(self.table) self.win.add(self.hbox) @@ -297,7 +298,7 @@ class GCTooltip(BaseTooltip): status = contact.status.strip() if status != '': # escape markup entities - status = helpers.reduce_chars_newlines(status, 100, 5) + status = helpers.reduce_chars_newlines(status, 300, 5) status = '' +\ gobject.markup_escape_text(status) + '' properties.append((status, None)) @@ -352,6 +353,7 @@ class GCTooltip(BaseTooltip): vertical_fill, 0, 0) else: label.set_markup(property[0]) + label.set_line_wrap(True) vcard_table.attach(label, 1, 3, vcard_current_row, vcard_current_row + 1, gtk.FILL, vertical_fill, 0) @@ -377,7 +379,7 @@ class RosterTooltip(NotificationAreaTooltip): self.create_table() if not contacts or len(contacts) == 0: # Tooltip for merged accounts row - accounts = helpers.get_accounts_info() + accounts = helpers.get_notification_icon_tooltip_dict() self.table.resize(2, 1) self.spacer_label = '' self.fill_table_with_accounts(accounts) @@ -441,8 +443,7 @@ class RosterTooltip(NotificationAreaTooltip): iconset = gajim.config.get('iconset') if not iconset: iconset = 'dcraven' - file_path = os.path.join(gajim.DATA_DIR, - 'iconsets', iconset, '16x16') + file_path = os.path.join(helpers.get_iconset_path(iconset), '16x16') contact_keys = contacts_dict.keys() contact_keys.sort() diff --git a/src/vcard.py b/src/vcard.py index 3c94dd4b6..3e11a3cb6 100644 --- a/src/vcard.py +++ b/src/vcard.py @@ -3,6 +3,7 @@ ## Copyright (C) 2003-2006 Yann Le Boulanger ## Copyright (C) 2005-2006 Nikos Kouremenos ## Copyright (C) 2006 Stefan Bethge +## Copyright (C) 2007 Lukas Petrovicky ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published @@ -70,27 +71,28 @@ class VcardWindow: self.account = account self.gc_contact = gc_contact - self.xml.get_widget('no_user_avatar_label').set_no_show_all(True) - self.xml.get_widget('no_user_avatar_label').hide() - self.xml.get_widget('PHOTO_image').set_no_show_all(True) - self.xml.get_widget('PHOTO_image').hide() - image = gtk.Image() - self.photo_button = self.xml.get_widget('PHOTO_button') - self.photo_button.set_image(image) - self.nophoto_button = self.xml.get_widget('NOPHOTO_button') + # Get real jid + if gc_contact: + if gc_contact.jid: + self.real_jid = gc_contact.jid + if gc_contact.resource: + self.real_jid += '/' + gc_contact.resource + else: + self.real_jid = gc_contact.get_full_jid() + else: + self.real_jid = contact.get_full_jid() + puny_jid = helpers.sanitize_filename(contact.jid) local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid) + \ '_local' for extension in ('.png', '.jpeg'): local_avatar_path = local_avatar_basepath + extension if os.path.isfile(local_avatar_path): + image = self.xml.get_widget('custom_avatar_image') image.set_from_file(local_avatar_path) - self.nophoto_button.set_no_show_all(True) - self.nophoto_button.hide() + image.show() + self.xml.get_widget('custom_avatar_label').show() break - else: - self.photo_button.set_no_show_all(True) - self.photo_button.hide() self.avatar_mime_type = None self.avatar_encoded = None self.vcard_arrived = False @@ -123,94 +125,6 @@ class VcardWindow: if win and ctrl.type_id != message_control.TYPE_GC: ctrl.show_avatar() - def on_NOPHOTO_button_clicked(self, button): - def on_ok(widget, path_to_file): - filesize = os.path.getsize(path_to_file) # in bytes - invalid_file = False - msg = '' - if os.path.isfile(path_to_file): - stat = os.stat(path_to_file) - if stat[6] == 0: - invalid_file = True - msg = _('File is empty') - else: - invalid_file = True - msg = _('File does not exist') - if invalid_file: - dialogs.ErrorDialog(_('Could not load image'), msg) - return - try: - pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file) - if filesize > 16384: # 16 kb - # get the image at 'notification size' - # and hope that user did not specify in ACE crazy size - pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip') - except gobject.GError, msg: # unknown format - # msg should be string, not object instance - msg = str(msg) - dialogs.ErrorDialog(_('Could not load image'), msg) - return - puny_jid = helpers.sanitize_filename(self.contact.jid) - path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png' - pixbuf.save(path_to_file, 'png') - self.dialog.destroy() - self.update_avatar_in_gui() - - # rescale it - pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard') - image = self.photo_button.get_image() - image.set_from_pixbuf(pixbuf) - self.photo_button.show() - self.nophoto_button.hide() - - def on_clear(widget): - self.dialog.destroy() - self.on_clear_button_clicked(widget) - - self.dialog = dialogs.AvatarChooserDialog(on_response_ok = on_ok, - on_response_clear = on_clear) - - def on_clear_button_clicked(self, widget): - # empty the image - image = self.photo_button.get_image() - image.set_from_pixbuf(None) - self.photo_button.hide() - self.nophoto_button.show() - # Delete file: - puny_jid = helpers.sanitize_filename(self.contact.jid) - path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png' - try: - os.remove(path_to_file) - except OSError: - gajim.log.debug('Cannot remove %s' % path_to_file) - self.update_avatar_in_gui() - - def on_PHOTO_button_press_event(self, widget, event): - '''If right-clicked, show popup''' - if event.button == 3 and self.avatar_encoded: # right click - menu = gtk.Menu() - - # Try to get pixbuf -# pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid) - -# if pixbuf: -# nick = self.contact.get_shown_name() -# menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS) -# menuitem.connect('activate', -# gtkgui_helpers.on_avatar_save_as_menuitem_activate, self.jid, -# None, nick + '.jpeg') -# menu.append(menuitem) - # show clear - menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR) - menuitem.connect('activate', self.on_clear_button_clicked) - menu.append(menuitem) - menu.connect('selection-done', lambda w:w.destroy()) - # show the menu - menu.show_all() - menu.popup(None, None, None, event.button, event.time) - elif event.button == 1: # left click - self.on_NOPHOTO_button_clicked(widget) - def on_vcard_information_window_destroy(self, widget): if self.update_progressbar_timeout_id is not None: gobject.source_remove(self.update_progressbar_timeout_id) @@ -334,7 +248,7 @@ class VcardWindow: def fill_status_label(self): if self.xml.get_widget('information_notebook').get_n_pages() < 5: return - contact_list = gajim.contacts.get_contact(self.account, self.contact.jid) + contact_list = gajim.contacts.get_contacts(self.account, self.contact.jid) connected_contact_list = [] for c in contact_list: if c.show not in ('offline', 'error'): @@ -415,8 +329,13 @@ class VcardWindow: self.contact.status = '' # Request list time status - gajim.connections[self.account].request_last_status_time(self.contact.jid, - self.contact.resource) + if self.gc_contact: + j, r = gajim.get_room_and_nick_from_fjid(self.real_jid) + gajim.connections[self.account].request_last_status_time(j, r, + self.contact.jid) + else: + gajim.connections[self.account].request_last_status_time( + self.contact.jid, self.contact.resource) # do not wait for os_info if contact is not connected or has error # additional check for observer is needed, as show is offline for him @@ -424,12 +343,17 @@ class VcardWindow: and not self.contact.is_observer(): self.os_info_arrived = True else: # Request os info if contact is connected - gobject.idle_add(gajim.connections[self.account].request_os_info, - self.contact.jid, self.contact.resource) + if self.gc_contact: + j, r = gajim.get_room_and_nick_from_fjid(self.real_jid) + gobject.idle_add(gajim.connections[self.account].request_os_info, + j, r, self.contact.jid) + else: + gobject.idle_add(gajim.connections[self.account].request_os_info, + self.contact.jid, self.contact.resource) self.os_info = {0: {'resource': self.contact.resource, 'client': '', 'os': ''}} i = 1 - contact_list = gajim.contacts.get_contact(self.account, self.contact.jid) + contact_list = gajim.contacts.get_contacts(self.account, self.contact.jid) if contact_list: for c in contact_list: if c.resource != self.contact.resource: @@ -454,7 +378,7 @@ class VcardWindow: self.fill_status_label() if self.gc_contact: - gajim.connections[self.account].request_vcard(self.contact.jid, + gajim.connections[self.account].request_vcard(self.real_jid, self.gc_contact.get_full_jid()) else: gajim.connections[self.account].request_vcard(self.contact.jid) @@ -522,7 +446,7 @@ class ZeroconfVcardWindow: def fill_status_label(self): if self.xml.get_widget('information_notebook').get_n_pages() < 2: return - contact_list = gajim.contacts.get_contact(self.account, self.contact.jid) + contact_list = gajim.contacts.get_contacts(self.account, self.contact.jid) # stats holds show and status message stats = '' one = True # Are we adding the first line ?