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

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomk <tomk@no-mail.com>2008-06-09 04:32:02 +0400
committertomk <tomk@no-mail.com>2008-06-09 04:32:02 +0400
commit6a15c9b9c9b53989a7ebd9c38854c2fe4bb5b922 (patch)
treef762415e84bdd9c5ade195582f8a4bf77237b0f4
parentcb2d6295358b2dc1e47238599727ec8d41a86af3 (diff)
initial cleanup of xmpppy perfomed, see #3260
-rw-r--r--AUTHORS2
-rw-r--r--data/glade/chat_control_popup_menu.glade45
-rw-r--r--data/glade/contact_otr_window.glade322
-rw-r--r--data/glade/roster_contact_context_menu.glade7
-rw-r--r--epydoc.conf29
-rw-r--r--po/de.po6402
-rw-r--r--po/fr.po30
-rw-r--r--src/chat_control.py135
-rw-r--r--src/common/config.py5
-rw-r--r--src/common/connection.py40
-rw-r--r--src/common/connection_handlers.py176
-rw-r--r--src/common/contacts.py27
-rw-r--r--src/common/dbus_support.py7
-rw-r--r--src/common/gajim.py7
-rw-r--r--src/common/helpers.py9
-rw-r--r--src/common/xmpp/__init__.py2
-rw-r--r--src/common/xmpp/auth.py306
-rw-r--r--src/common/xmpp/auth_nb.py56
-rw-r--r--src/common/xmpp/browser.py216
-rw-r--r--src/common/xmpp/client.py300
-rw-r--r--src/common/xmpp/client_bosh.py194
-rw-r--r--src/common/xmpp/client_nb.py37
-rw-r--r--src/common/xmpp/commands.py330
-rw-r--r--src/common/xmpp/dispatcher.py383
-rw-r--r--src/common/xmpp/examples/run_client_bosh.py89
-rw-r--r--src/common/xmpp/features.py184
-rw-r--r--src/common/xmpp/features_nb.py6
-rw-r--r--src/common/xmpp/filetransfer.py199
-rw-r--r--src/common/xmpp/roster.py195
-rw-r--r--src/common/xmpp/roster_nb.py168
-rw-r--r--src/common/xmpp/session.py350
-rw-r--r--src/common/xmpp/transports.py289
-rw-r--r--src/common/xmpp/transports_nb.py22
-rw-r--r--src/config.py25
-rw-r--r--src/conversation_textview.py11
-rw-r--r--src/features_window.py9
-rwxr-xr-xsrc/gajim.py326
-rw-r--r--src/groupchat_control.py12
-rw-r--r--src/history_window.py74
-rw-r--r--src/message_control.py65
-rw-r--r--src/message_window.py6
-rw-r--r--src/osx/__init__.py8
-rw-r--r--src/otr_windows.py357
-rw-r--r--src/roster_window.py752
-rw-r--r--src/session.py5
-rw-r--r--src/statusicon.py10
-rw-r--r--src/systray.py1
-rw-r--r--src/tictactoe.py427
-rw-r--r--test/mocks.py68
-rw-r--r--test/test_sessions.py69
50 files changed, 4286 insertions, 8508 deletions
diff --git a/AUTHORS b/AUTHORS
index f1b6f72b3..0b64f2b68 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,10 +6,10 @@ Travis Shirk (travis AT pobox.com)
Stephan Erb (steve-e AT h3c.de)
Julien Pivotto (roidelapluie AT gmail.com)
Jonathan Schleifer (js-gajim AT webkeks.org)
+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/data/glade/chat_control_popup_menu.glade b/data/glade/chat_control_popup_menu.glade
index d53077869..1c923adbd 100644
--- a/data/glade/chat_control_popup_menu.glade
+++ b/data/glade/chat_control_popup_menu.glade
@@ -72,51 +72,6 @@
</widget>
</child>
<child>
- <widget class="GtkMenuItem" id="otr_submenu">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="label" translatable="yes">Off-the-Record Encryption</property>
- <property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="otr_submenu_menu">
- <child>
- <widget class="GtkMenuItem" id="otr_settings_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">OTR settings / fingerprint</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_otr_settings_menuitem_activate"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="smp_otr_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Authenticate contact</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_smp_otr_menuitem_activate"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="start_otr_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Start / Refresh OTR</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_start_otr_menuitem_activate"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="end_otr_menuitem">
- <property name="visible">True</property>
- <property name="sensitive">False</property>
- <property name="label" translatable="yes">End OTR </property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="_on_end_otr_menuitem_activate"/>
- </widget>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="no_show_all">True</property>
diff --git a/data/glade/contact_otr_window.glade b/data/glade/contact_otr_window.glade
deleted file mode 100644
index e0f5b865c..000000000
--- a/data/glade/contact_otr_window.glade
+++ /dev/null
@@ -1,322 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.2 on Wed Mar 26 13:56:40 2008 -->
-<glade-interface>
- <widget class="GtkWindow" id="otr_settings_window">
- <property name="resizable">False</property>
- <child>
- <widget class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <child>
- <widget class="GtkNotebook" id="notebook1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <widget class="GtkVBox" id="otr_fp_vbox">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="spacing">5</property>
- <property name="homogeneous">True</property>
- <child>
- <widget class="GtkLabel" id="our_fp_label">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Your fingerprint:
-&lt;span weight="bold" face="monospace"&gt;01234567 89ABCDEF 01234567 89ABCDEF 01234567&lt;/span&gt;</property>
- <property name="use_markup">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="their_fp_label">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Purported fingerprint for asdfasdf@xyzxyzxyz.de:
-&lt;span weight="bold" face="monospace"&gt;01234567 89ABCDEF 01234567 89ABCDEF 01234567&lt;/span&gt;</property>
- <property name="use_markup">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <child>
- <widget class="GtkComboBox" id="verified_combobox">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="active">1</property>
- <property name="items">I have NOT
-I have</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="xalign">0.20000000298023224</property>
- <property name="label" translatable="yes">verified that the purported fingerprint is in fact the correct fingerprint for that contact.</property>
- <property name="wrap">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Authentication</property>
- </widget>
- <packing>
- <property name="type">tab</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkFrame" id="frame2">
- <property name="visible">True</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">GTK_SHADOW_NONE</property>
- <child>
- <widget class="GtkVBox" id="otr_settings_vbox">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="spacing">5</property>
- <property name="homogeneous">True</property>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_allow_v1_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">OTR version 1 allowed</property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_allow_v2_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">OTR version 2 allowed</property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_require_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Encryption required</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_send_tag_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Show others we understand OTR</property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_start_on_tag_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Automatically initiate encryption if partner understands OTR</property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_policy_start_on_error_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Automatically start OTR when an OTR error occured</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">5</property>
- </packing>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkCheckButton" id="otr_default_checkbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Use the default settings</property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="type">label_item</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="label" translatable="yes">OTR Settings</property>
- </widget>
- <packing>
- <property name="type">tab</property>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkHButtonBox" id="hbuttonbox1">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
- <child>
- <widget class="GtkButton" id="settings_cancel_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- </widget>
- </child>
- <child>
- <widget class="GtkButton" id="settings_ok_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <widget class="GtkWindow" id="otr_smp_window">
- <property name="resizable">False</property>
- <child>
- <widget class="GtkVBox" id="vbox3">
- <property name="visible">True</property>
- <child>
- <widget class="GtkVBox" id="vbox4">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <property name="spacing">5</property>
- <child>
- <widget class="GtkLabel" id="desc_label">
- <property name="visible">True</property>
- <property name="label" translatable="yes">label</property>
- <property name="use_markup">True</property>
- <property name="wrap">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkEntry" id="secret_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox3">
- <property name="visible">True</property>
- <property name="border_width">5</property>
- <child>
- <widget class="GtkProgressBar" id="progressbar">
- <property name="visible">True</property>
- <property name="text" translatable="yes"></property>
- </widget>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHButtonBox" id="hbuttonbox2">
- <property name="visible">True</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
- <child>
- <widget class="GtkButton" id="smp_cancel_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-cancel</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- </widget>
- </child>
- <child>
- <widget class="GtkButton" id="smp_ok_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-ok</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
-</glade-interface>
diff --git a/data/glade/roster_contact_context_menu.glade b/data/glade/roster_contact_context_menu.glade
index 2a1955007..f873e7f77 100644
--- a/data/glade/roster_contact_context_menu.glade
+++ b/data/glade/roster_contact_context_menu.glade
@@ -92,13 +92,6 @@
</widget>
</child>
<child>
- <widget class="GtkImageMenuItem" id="tictactoe_menuitem">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Play Tic Tac Toe</property>
- <property name="use_underline">True</property>
- </widget>
- </child>
- <child>
<widget class="GtkImageMenuItem" id="manage_contact">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
diff --git a/epydoc.conf b/epydoc.conf
new file mode 100644
index 000000000..b2a1c0565
--- /dev/null
+++ b/epydoc.conf
@@ -0,0 +1,29 @@
+[epydoc]
+
+# Information about the project.
+name: Gajim
+url: http://gajim.org
+
+verbosity: 3
+imports: yes
+redundant-details: yes
+docformat: restructuredtext
+# top: gajim
+
+# The list of modules to document. Modules can be named using
+# dotted names, module filenames, or package directory names.
+# This option may be repeated.
+modules: src/* test/*
+
+# Write html output to the directory "apidocs"
+#output: pdf
+output: html
+target: apidocs/
+
+# Include all automatically generated graphs. These graphs are
+# generated using Graphviz dot.
+graph: all
+dotpath: /usr/bin/dot
+graph-font: Sans
+graph-font-size: 10
+
diff --git a/po/de.po b/po/de.po
index 653485d1a..93fdff9c8 100644
--- a/po/de.po
+++ b/po/de.po
@@ -3,7 +3,7 @@
# This file is distributed under the same license as the gajim package.
# Fridtjof Busse <fridtjof@fbunet.de>, 2005, 2006.
# Benjamin Drung <benjamin.drung@gmail.com>, 2007, 2008.
-# Fabian Fingerle <fabian@datensalat.eu>, 2007.
+# Fabian Fingerle <fabian@datensalat.eu>, 2007, 2008.
# Sebastian Schäfer <sarek@uliweb.de>, 2007.
# Nico Gulden <cilugnedon@gmx.de>, 2007, 2008.
#
@@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gajim 0.11\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-16 10:15+0200\n"
+"POT-Creation-Date: 2008-06-03 07:50+0200\n"
"PO-Revision-Date: 2008-03-24 20:40+0100\n"
"Last-Translator: Nico Gulden <cilugnedon@gmx.de>\n"
"Language-Team: German <translators@gajim.org>\n"
@@ -103,8 +103,8 @@ msgid ""
"Add this certificate to the list of trusted certificates.\n"
"SHA1 fingerprint of the certificate:\n"
msgstr ""
-"Dieses Zertifikat der Liste von Vertrauenswürdigen Zertifikaten hinzufügen\n"
-"SHA1 Fingerabdruck von dem Zertifikat:\n"
+"Dieses Zertifikat der Liste vertrauenswürdiger Zertifikate hinzufügen\n"
+"SHA1-Fingerprint dieses Zertifikates:\n"
#: ../data/glade/account_creation_wizard_window.glade.h:10
msgid "Click to see features (like MSN, ICQ transports) of jabber servers"
@@ -299,9 +299,9 @@ msgstr "Persönliche Informationen bearbeiten ..."
#. No configured account
#: ../data/glade/account_modification_window.glade.h:16
#: ../data/glade/accounts_window.glade.h:20
-#: ../data/glade/roster_window.glade.h:6 ../src/notify.py:498
-#: ../src/notify.py:529 ../src/notify.py:541 ../src/common/helpers.py:1083
-#: ../src/common/helpers.py:1095
+#: ../data/glade/roster_window.glade.h:6 ../src/common/helpers.py:1083
+#: ../src/common/helpers.py:1095 ../src/notify.py:498 ../src/notify.py:529
+#: ../src/notify.py:541
msgid "Gajim"
msgstr "Gajim"
@@ -311,15 +311,15 @@ msgstr "Gajim"
#. General group cannot be changed
#: ../data/glade/account_modification_window.glade.h:17
#: ../data/glade/accounts_window.glade.h:21
-#: ../data/glade/preferences_window.glade.h:50
-#: ../data/glade/zeroconf_properties_window.glade.h:7 ../src/dialogs.py:100
-#: ../src/dialogs.py:108 ../src/dialogs.py:162 ../src/roster_window.py:184
-#: ../src/roster_window.py:360 ../src/roster_window.py:641
-#: ../src/roster_window.py:685 ../src/roster_window.py:973
-#: ../src/roster_window.py:1193 ../src/roster_window.py:1351
-#: ../src/roster_window.py:2515 ../src/roster_window.py:4639
-#: ../src/roster_window.py:4766 ../src/roster_window.py:4935
-#: ../src/common/contacts.py:322
+#: ../data/glade/preferences_window.glade.h:47
+#: ../data/glade/zeroconf_properties_window.glade.h:7
+#: ../src/common/contacts.py:323 ../src/dialogs.py:100 ../src/dialogs.py:108
+#: ../src/dialogs.py:162 ../src/roster_window.py:184
+#: ../src/roster_window.py:361 ../src/roster_window.py:638
+#: ../src/roster_window.py:693 ../src/roster_window.py:958
+#: ../src/roster_window.py:1207 ../src/roster_window.py:1382
+#: ../src/roster_window.py:2569 ../src/roster_window.py:4718
+#: ../src/roster_window.py:4848 ../src/roster_window.py:5013
msgid "General"
msgstr "Allgemein"
@@ -384,21 +384,21 @@ msgstr "Informationen über Sie, wie sie auf dem Server gespeichert sind"
#: ../data/glade/account_modification_window.glade.h:27
#: ../data/glade/accounts_window.glade.h:36
-#: ../data/glade/zeroconf_properties_window.glade.h:16 ../src/config.py:1611
-#: ../src/config.py:2098 ../src/config.py:3692
+#: ../data/glade/zeroconf_properties_window.glade.h:16 ../src/config.py:1523
+#: ../src/config.py:2017 ../src/config.py:3619
msgid "No key selected"
msgstr "Kein Schlüssel gewählt"
#. None means no proxy profile selected
#. GPG Key
#: ../data/glade/account_modification_window.glade.h:29
-#: ../data/glade/accounts_window.glade.h:38 ../src/config.py:1199
-#: ../src/config.py:1270 ../src/config.py:1519 ../src/config.py:1524
-#: ../src/config.py:2005 ../src/config.py:2083 ../src/config.py:2097
-#: ../src/config.py:3190 ../src/config.py:3258 ../src/config.py:3682
-#: ../src/config.py:3691 ../src/dialogs.py:304 ../src/dialogs.py:306
-#: ../src/roster_window.py:2597 ../src/roster_window.py:2604
-#: ../src/roster_window.py:2611
+#: ../data/glade/accounts_window.glade.h:38 ../src/config.py:1112
+#: ../src/config.py:1183 ../src/config.py:1431 ../src/config.py:1436
+#: ../src/config.py:1924 ../src/config.py:2002 ../src/config.py:2016
+#: ../src/config.py:3115 ../src/config.py:3183 ../src/config.py:3609
+#: ../src/config.py:3618 ../src/dialogs.py:304 ../src/dialogs.py:306
+#: ../src/roster_window.py:2645 ../src/roster_window.py:2652
+#: ../src/roster_window.py:2659
msgid "None"
msgstr "Kein"
@@ -485,6 +485,7 @@ msgstr "Konto-Status mit globalem _Status abgleichen"
#: ../data/glade/account_modification_window.glade.h:43
#: ../data/glade/accounts_window.glade.h:51
+#: ../data/glade/synchronise_contacts_dialog.glade.h:2
#: ../data/glade/synchronise_select_account_dialog.glade.h:2
msgid "Synchronise contacts"
msgstr "Kontakte synchronisieren"
@@ -566,7 +567,7 @@ msgstr ""
#: ../data/glade/accounts_window.glade.h:32
#: ../data/glade/zeroconf_information_window.glade.h:4
-#: ../data/glade/zeroconf_properties_window.glade.h:13 ../src/dialogs.py:614
+#: ../data/glade/zeroconf_properties_window.glade.h:13 ../src/dialogs.py:643
msgid "Jabber ID:"
msgstr "Jabber-ID:"
@@ -581,7 +582,7 @@ msgid "Mer_ge accounts"
msgstr "Konten _zusammenführen"
#. Rename
-#: ../data/glade/accounts_window.glade.h:44 ../src/roster_window.py:4718
+#: ../data/glade/accounts_window.glade.h:44 ../src/roster_window.py:4800
msgid "Re_name"
msgstr "_Umbenennen"
@@ -783,7 +784,7 @@ msgid "<b>Conditions</b>"
msgstr "<b>Ereignisse</b>"
#: ../data/glade/advanced_notifications_window.glade.h:4
-#: ../data/glade/preferences_window.glade.h:16
+#: ../data/glade/preferences_window.glade.h:15
msgid "<b>Sounds</b>"
msgstr "<b>Klänge</b>"
@@ -955,6 +956,28 @@ msgstr "Sie haben einen neuen Eintrag erhalten:"
msgid "Blocked Contacts"
msgstr "blockierte Kontakte"
+#: ../data/glade/change_activity_dialog.glade.h:1
+msgid "General:"
+msgstr "Allgemein:"
+
+#: ../data/glade/change_activity_dialog.glade.h:2
+#: ../data/glade/change_mood_dialog.glade.h:1
+msgid "Message:"
+msgstr "Nachricht"
+
+#: ../data/glade/change_activity_dialog.glade.h:3
+msgid "Set Activity"
+msgstr "Aktivität setzen"
+
+#: ../data/glade/change_activity_dialog.glade.h:4
+msgid "Specific:"
+msgstr "Detailliert:"
+
+#: ../data/glade/change_mood_dialog.glade.h:2
+#: ../data/glade/manage_proxies_window.glade.h:8
+msgid "Type:"
+msgstr "Art:"
+
#: ../data/glade/change_password_dialog.glade.h:1
msgid "Change Password"
msgstr "Passwort ändern"
@@ -984,7 +1007,7 @@ msgid "Join _Group Chat"
msgstr "_Gruppenchat beitreten"
#: ../data/glade/chat_context_menu.glade.h:2
-#: ../data/glade/roster_contact_context_menu.glade.h:13
+#: ../data/glade/roster_contact_context_menu.glade.h:12
msgid "_Add to Roster..."
msgstr "Zur Kont_aktliste hinzufügen ..."
@@ -1016,56 +1039,51 @@ msgid "Authenticate contact"
msgstr "Kontakt automatisch autorisieren"
#: ../data/glade/chat_control_popup_menu.glade.h:2
-msgid "Click to see past conversations with this contact"
-msgstr "Klicken, um die früheren Unterhaltungen mit diesem Kontakt zu sehen"
-
-#: ../data/glade/chat_control_popup_menu.glade.h:3
msgid "End OTR "
-msgstr ""
+msgstr "OTR beenden "
-#: ../data/glade/chat_control_popup_menu.glade.h:4
+#: ../data/glade/chat_control_popup_menu.glade.h:3
msgid "Invite _Contacts"
msgstr "_Kontakte einladen"
-#: ../data/glade/chat_control_popup_menu.glade.h:5
+#: ../data/glade/chat_control_popup_menu.glade.h:4
msgid "OTR settings / fingerprint"
-msgstr ""
+msgstr "OTR Einstellungen / Fingerprint"
-#: ../data/glade/chat_control_popup_menu.glade.h:6
-#, fuzzy
+#: ../data/glade/chat_control_popup_menu.glade.h:5
msgid "Off-the-Record Encryption"
-msgstr "Open_PGP-Verschlüsselung"
+msgstr "OTR-Verschlüsselung"
-#: ../data/glade/chat_control_popup_menu.glade.h:7
+#: ../data/glade/chat_control_popup_menu.glade.h:6
#: ../data/glade/gc_occupants_menu.glade.h:3
#: ../data/glade/zeroconf_contact_context_menu.glade.h:4
msgid "Send _File"
msgstr "_Datei senden"
-#: ../data/glade/chat_control_popup_menu.glade.h:8
+#: ../data/glade/chat_control_popup_menu.glade.h:7
msgid "Start / Refresh OTR"
msgstr ""
-#: ../data/glade/chat_control_popup_menu.glade.h:9
+#: ../data/glade/chat_control_popup_menu.glade.h:8
msgid "Toggle End to End Encryption"
-msgstr "Verschlüsselung (ESessions) aktivieren"
+msgstr "Punkt-zu-Punkt-Verschlüsselung (ESessions) aktivieren"
-#: ../data/glade/chat_control_popup_menu.glade.h:10
+#: ../data/glade/chat_control_popup_menu.glade.h:9
msgid "Toggle Open_PGP Encryption"
msgstr "Open_PGP-Verschlüsselung aktivieren"
-#: ../data/glade/chat_control_popup_menu.glade.h:11
+#: ../data/glade/chat_control_popup_menu.glade.h:10
#: ../data/glade/gc_occupants_menu.glade.h:4
msgid "_Add to Roster"
msgstr "Zur Kont_aktliste hinzufügen"
-#: ../data/glade/chat_control_popup_menu.glade.h:12
-#: ../data/glade/gc_control_popup_menu.glade.h:7
+#: ../data/glade/chat_control_popup_menu.glade.h:11
+#: ../data/glade/gc_control_popup_menu.glade.h:6
#: ../data/glade/gc_occupants_menu.glade.h:7
-#: ../data/glade/roster_contact_context_menu.glade.h:17
+#: ../data/glade/roster_contact_context_menu.glade.h:16
#: ../data/glade/roster_window.glade.h:20
#: ../data/glade/zeroconf_contact_context_menu.glade.h:6
-#: ../src/roster_window.py:5373
+#: ../src/roster_window.py:5456
msgid "_History"
msgstr "_Verlauf"
@@ -1273,26 +1291,22 @@ msgid "Change _Subject..."
msgstr "_Thema ändern ..."
#: ../data/glade/gc_control_popup_menu.glade.h:3
-msgid "Click to see past conversation in this room"
-msgstr "Klicken, um die früheren Unterhaltungen in diesem Raum zu sehen"
-
-#: ../data/glade/gc_control_popup_menu.glade.h:4
msgid "Configure _Room..."
msgstr "_Raum einrichten ..."
-#: ../data/glade/gc_control_popup_menu.glade.h:5
+#: ../data/glade/gc_control_popup_menu.glade.h:4
msgid "_Bookmark"
msgstr "Raum zu _Lesezeichen hinzufügen"
-#: ../data/glade/gc_control_popup_menu.glade.h:6
+#: ../data/glade/gc_control_popup_menu.glade.h:5
msgid "_Destroy Room"
msgstr "_Raum auflösen"
-#: ../data/glade/gc_control_popup_menu.glade.h:8
+#: ../data/glade/gc_control_popup_menu.glade.h:7
msgid "_Manage Room"
msgstr "Raum verwalten"
-#: ../data/glade/gc_control_popup_menu.glade.h:9
+#: ../data/glade/gc_control_popup_menu.glade.h:8
msgid "_Minimize on close"
msgstr "Beim Schließen _minimieren"
@@ -1341,8 +1355,8 @@ msgid "From"
msgstr "Von"
#. holds subject
-#: ../data/glade/groups_post_window.glade.h:3 ../src/history_manager.py:164
-#: ../src/history_manager.py:195
+#: ../data/glade/groups_post_window.glade.h:3 ../src/history_manager.py:165
+#: ../src/history_manager.py:196
msgid "Subject"
msgstr "Betreff"
@@ -1392,24 +1406,23 @@ msgstr ""
msgid "_Search Database"
msgstr "_Suche Datenbank"
-#: ../data/glade/history_window.glade.h:1 ../src/history_window.py:300
+#: ../data/glade/history_window.glade.h:1 ../src/history_window.py:306
msgid "Conversation History"
msgstr "Unterhaltungsverlauf"
#: ../data/glade/history_window.glade.h:2
-#, fuzzy
msgid ""
"Enter JID or Contact name\n"
"Groupchat Histories\n"
"All Chat Histories"
msgstr ""
-"Aktueller Verlauf\n"
+"JID oder Kontaktname eingeben\n"
+"Gruppenchat-Verlauf\n"
"Gesamter Chatverlauf"
#: ../data/glade/history_window.glade.h:5
-#, fuzzy
msgid "Search:"
-msgstr "Suche"
+msgstr "Suche:"
#: ../data/glade/history_window.glade.h:6
#: ../data/glade/zeroconf_information_window.glade.h:10
@@ -1429,7 +1442,7 @@ msgstr "Ablehnen"
msgid "Invitation Received"
msgstr "Einladung empfangen"
-#: ../data/glade/join_groupchat_window.glade.h:1 ../src/dialogs.py:1635
+#: ../data/glade/join_groupchat_window.glade.h:1 ../src/dialogs.py:1664
msgid "Join Group Chat"
msgstr "Betrete Gruppenchat"
@@ -1493,6 +1506,18 @@ msgstr "Server:"
msgid "Title:"
msgstr "Titel:"
+#: ../data/glade/manage_pep_services_window.glade.h:1
+msgid "PEP Service Configuration"
+msgstr "PEP-Dienst Konfiguration"
+
+#: ../data/glade/manage_pep_services_window.glade.h:2
+msgid "_Configure"
+msgstr "_Einstellen"
+
+#: ../data/glade/manage_pep_services_window.glade.h:3
+msgid "gtk-delete"
+msgstr "gtk-delete"
+
#: ../data/glade/manage_proxies_window.glade.h:1
msgid "<b>Properties</b>"
msgstr "<b>Eigenschaften</b>"
@@ -1521,10 +1546,6 @@ msgstr "Name:"
msgid "Pass_word:"
msgstr "Pass_wort:"
-#: ../data/glade/manage_proxies_window.glade.h:8
-msgid "Type:"
-msgstr "Art:"
-
#: ../data/glade/manage_proxies_window.glade.h:9
msgid "Use authentication"
msgstr "Verwende Authentifizierung"
@@ -1534,47 +1555,44 @@ msgid "_Host:"
msgstr "_Server:"
#: ../data/glade/message_window.glade.h:1
-#, fuzzy
-msgid "Add this contact to roster (Ctrl-D)"
-msgstr "Fügt Kontakt zum Roster hinzu"
+msgid "Add this contact to roster (Ctrl+D)"
+msgstr "Kontakt zum Roster hinzufügen (STRG+D)"
#: ../data/glade/message_window.glade.h:2
-msgid "Bookmark this room (Ctrl-B)"
+msgid "Bookmark this room (Ctrl+B)"
msgstr ""
#: ../data/glade/message_window.glade.h:3
-msgid "Browse the chat history (Ctrl-H)"
+msgid "Browse the chat history (Ctrl+H)"
msgstr ""
#: ../data/glade/message_window.glade.h:4
-msgid "Change the room's subject (Ctrl-T)"
+msgid "Change the room's subject (Ctrl+T)"
msgstr ""
#: ../data/glade/message_window.glade.h:5
-msgid "Change your nickname (Ctrl-N)"
+msgid "Change your nickname (Ctrl+N)"
msgstr ""
#: ../data/glade/message_window.glade.h:6
-#, fuzzy
-msgid "Invite contacts to the conversation (Ctrl-G)"
-msgstr "beobachtet diese Unterhaltung"
+msgid "Invite contacts to the conversation (Ctrl+G)"
+msgstr "Kontakt zu Unterhaltung einladen (STRG+G)"
#: ../data/glade/message_window.glade.h:7
-msgid "Send a file (Ctrl-F)"
+msgid "Send a file (Ctrl+F)"
msgstr ""
#: ../data/glade/message_window.glade.h:8
-#, fuzzy
-msgid "Show a list of emoticons (Alt-M)"
-msgstr "Klicken Sie, um ein Emoticon einzufügen (Alt+M)"
+msgid "Show a list of emoticons (Alt+M)"
+msgstr "Smilie-Liste anzeigen (Alt+M)"
#: ../data/glade/message_window.glade.h:9
-msgid "Show a menu of advanced functions (Alt-A)"
-msgstr ""
+msgid "Show a menu of advanced functions (Alt+A)"
+msgstr "Liste erweiterter Funktionen anzeigen (Alt+A)"
#: ../data/glade/message_window.glade.h:10
-msgid "Show the contact's profile (Ctrl-I)"
-msgstr ""
+msgid "Show the contact's profile (Ctrl+I)"
+msgstr "Benutzerprofil anzeigen (STRG+I)"
#. Make sure the character after "_" is not M/m (conflicts with Alt+M that is supposed to show the Emoticon Selector)
#: ../data/glade/message_window.glade.h:12
@@ -1633,38 +1651,30 @@ msgid "<b>Privacy</b>"
msgstr "<i>Privatsphäre</i>"
#: ../data/glade/preferences_window.glade.h:14
-msgid "<b>Publish and Subscribe</b>"
-msgstr "<b>Veröffentlichen und Abonnieren</b>"
-
-#: ../data/glade/preferences_window.glade.h:15
msgid "<b>Roster Appearance</b>"
msgstr "<b>Roster-Aussehen</b>"
-#: ../data/glade/preferences_window.glade.h:17
+#: ../data/glade/preferences_window.glade.h:16
msgid "<b>Status Messages</b>"
msgstr "<b>Status-Nachrichten</b>"
-#: ../data/glade/preferences_window.glade.h:18
+#: ../data/glade/preferences_window.glade.h:17
msgid "<b>Themes</b>"
msgstr "<b>Themen</b>"
-#: ../data/glade/preferences_window.glade.h:19
+#: ../data/glade/preferences_window.glade.h:18
msgid "<b>Visual Notifications</b>"
msgstr "<b>Visuelle Benachrichtigungen</b>"
-#: ../data/glade/preferences_window.glade.h:20 ../src/roster_window.py:4484
-msgid "Activity"
-msgstr "Aktivität"
-
-#: ../data/glade/preferences_window.glade.h:21
+#: ../data/glade/preferences_window.glade.h:19
msgid "Advanced"
msgstr "Erweitert"
-#: ../data/glade/preferences_window.glade.h:22
+#: ../data/glade/preferences_window.glade.h:20
msgid "Advanced..."
msgstr "Erweitert ..."
-#: ../data/glade/preferences_window.glade.h:23
+#: ../data/glade/preferences_window.glade.h:21
msgid ""
"All chat states\n"
"Composing only\n"
@@ -1674,25 +1684,21 @@ msgstr ""
"Nur beim Schreiben\n"
"Deaktiviert"
-#: ../data/glade/preferences_window.glade.h:26
+#: ../data/glade/preferences_window.glade.h:24
msgid "Allow _OS information to be sent"
msgstr "Sende _Betriebssystem-Information"
-#: ../data/glade/preferences_window.glade.h:27
-msgid "Allow others to see your:"
-msgstr "Erlaube anderen zu sehen:"
-
-#: ../data/glade/preferences_window.glade.h:28
+#: ../data/glade/preferences_window.glade.h:25
msgid "Allow popup/notifications when I'm _away/na/busy/invisible"
msgstr ""
"Popups/Benachrichtigungen _zulassen, wenn ich abwesend/NA/beschäftigt/"
"unsichtbar bin"
-#: ../data/glade/preferences_window.glade.h:29
+#: ../data/glade/preferences_window.glade.h:26
msgid "Ask status message when I:"
msgstr "Erfrage Status-Nachricht beim: "
-#: ../data/glade/preferences_window.glade.h:30
+#: ../data/glade/preferences_window.glade.h:27
msgid ""
"Autodetect on every Gajim startup\n"
"Always use GNOME default applications\n"
@@ -1706,19 +1712,19 @@ msgstr ""
"Immer XFCE4 Standard-Anwendungen verwenden\n"
"Benutzerdefiniert"
-#: ../data/glade/preferences_window.glade.h:35
+#: ../data/glade/preferences_window.glade.h:32
msgid "Chat message:"
msgstr "Chat-Nachricht:"
-#: ../data/glade/preferences_window.glade.h:36
+#: ../data/glade/preferences_window.glade.h:33
msgid "Check on startup if Gajim is the _default Jabber client"
msgstr "Beim Starten überprüfen, ob Gajim der _Standard-Jabber-Client ist"
-#: ../data/glade/preferences_window.glade.h:37
+#: ../data/glade/preferences_window.glade.h:34
msgid "Configure color and font of the interface"
msgstr "Farbe und Schriftart für die Oberfläche konfigurieren"
-#: ../data/glade/preferences_window.glade.h:38
+#: ../data/glade/preferences_window.glade.h:35
msgid ""
"Detached roster with detached chats\n"
"Detached roster with single chat\n"
@@ -1732,23 +1738,23 @@ msgstr ""
"seperate Kontaktliste gruppiert nach Benutzerkonto\n"
"seperate Kontaktliste gruppiert nach Typ"
-#: ../data/glade/preferences_window.glade.h:43
+#: ../data/glade/preferences_window.glade.h:40
msgid "Display _extra email details"
msgstr "Zeige zusätzliche _E-Mail-Details"
-#: ../data/glade/preferences_window.glade.h:44
+#: ../data/glade/preferences_window.glade.h:41
msgid "Display a_vatars of contacts in roster"
msgstr "Zeige A_vatare von Kontakten im Roster"
-#: ../data/glade/preferences_window.glade.h:45
+#: ../data/glade/preferences_window.glade.h:42
msgid "Display status _messages of contacts in roster"
msgstr "Zeige die Status_nachrichten von Kontakten im Roster"
-#: ../data/glade/preferences_window.glade.h:46
+#: ../data/glade/preferences_window.glade.h:43
msgid "Emoticons:"
msgstr "Emoticons:"
-#: ../data/glade/preferences_window.glade.h:47
+#: ../data/glade/preferences_window.glade.h:44
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 "
@@ -1758,7 +1764,7 @@ msgstr ""
"betrifft, empfangen und versenden. Hier können Sie definieren, welche "
"Chatstatus Sie in Ihrem Chatfenster anzeigen möchten."
-#: ../data/glade/preferences_window.glade.h:48
+#: ../data/glade/preferences_window.glade.h:45
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 "
@@ -1768,7 +1774,7 @@ msgstr ""
"betrifft, empfangen und versenden. Hier können Sie definieren, welche "
"Chatstates Sie Ihrem Gegenüber senden möchten."
-#: ../data/glade/preferences_window.glade.h:49
+#: ../data/glade/preferences_window.glade.h:46
msgid ""
"Gajim will notify you via a popup window in the bottom right of the screen "
"about contacts that just signed out"
@@ -1776,25 +1782,18 @@ msgstr ""
"Gajim wird Sie über ein Popup-Fenster in der rechten unteren Bildschirmecke "
"über Kontakte informieren, die sich gerade abgemeldet haben"
-#: ../data/glade/preferences_window.glade.h:51
+#: ../data/glade/preferences_window.glade.h:48
msgid "Hide all buttons in chat windows"
msgstr "Versteckt alle Knöpfe im Chat-Fenster"
-#: ../data/glade/preferences_window.glade.h:52
-msgid ""
-"If checked, Gajim log GPG or E2E encrypted messages. However, when using E2E "
-"encryption the remote site has to agree on logging. If the other side has "
-"declined logging, your messages will NOT be logged."
-msgstr ""
-
-#: ../data/glade/preferences_window.glade.h:53
+#: ../data/glade/preferences_window.glade.h:49
msgid ""
"If checked, Gajim will allow others to detect the operation system you are "
"using"
msgstr ""
"Wenn aktiviert, erlaubt Gajim anderen dein Betriebssystem festzustellen"
-#: ../data/glade/preferences_window.glade.h:54
+#: ../data/glade/preferences_window.glade.h:50
msgid ""
"If checked, Gajim will also include information about the sender of the new "
"emails"
@@ -1802,20 +1801,22 @@ msgstr ""
"Wenn aktiviert, wird Gajim auch Informationen über den Absender von neuen E-"
"Mails hinzufügen."
-#: ../data/glade/preferences_window.glade.h:55
+#: ../data/glade/preferences_window.glade.h:51
msgid ""
"If checked, Gajim will change status to Away when the computer is unused."
msgstr ""
"Wenn aktiviert, wird Gajim den Status in Abwesend ändern, wenn der Computer "
"nicht genutzt wird."
-#: ../data/glade/preferences_window.glade.h:56
+#: ../data/glade/preferences_window.glade.h:52
msgid ""
"If checked, Gajim will change status to Not Available when the computer has "
"not been used even longer"
msgstr ""
+"Wenn aktiviert, wird Gajim den Status in Nicht Verfügbar ändern, wenn der "
+"Computer länger nicht genutzt wird."
-#: ../data/glade/preferences_window.glade.h:57
+#: ../data/glade/preferences_window.glade.h:53
msgid ""
"If checked, Gajim will display avatars of contacts in roster window and in "
"group chats"
@@ -1823,7 +1824,7 @@ msgstr ""
"Wenn aktiviert, wird Gajim einen Avatar im Roster-Fenster und in "
"Gruppenchats einblenden"
-#: ../data/glade/preferences_window.glade.h:58
+#: ../data/glade/preferences_window.glade.h:54
msgid ""
"If checked, Gajim will display status messages of contacts under the contact "
"name in roster window and in group chats"
@@ -1831,14 +1832,18 @@ msgstr ""
"Wenn aktiviert, wird Gajim für jeden Kontakt unter dem Kontaktnamen im "
"Roster und im Gruppenchat-Fenster die Statusnachricht anzeigen"
-#: ../data/glade/preferences_window.glade.h:59
+#: ../data/glade/preferences_window.glade.h:55
msgid ""
"If checked, Gajim will highlight spelling errors in input fields of chat "
"windows. If no language is explicitly set via right click on the input "
"field, the default language will be used for this contact or group chat."
msgstr ""
+"Wenn aktiviert, wird Gajim Rechtschreibfehler im Chat-Eingabefenster "
+"hervorheben. Wenn keine Sprache durch setzen mittels Rechtsklick im "
+"Eingabefeld definiert wurde, wird die Standardsprache für diesen Kontakt "
+"oder Gruppenchat verwendet"
-#: ../data/glade/preferences_window.glade.h:60
+#: ../data/glade/preferences_window.glade.h:56
msgid ""
"If checked, Gajim will ignore incoming events from unauthorized contacts. "
"Use with caution, because it blocks all messages from any contact that is "
@@ -1848,7 +1853,18 @@ msgstr ""
"Kontakten ignorieren. Mit Vorsicht benutzen, weil alle Nachrichten von "
"Kontakten blockiert werden, die nicht in deiner Kontaktliste sind"
-#: ../data/glade/preferences_window.glade.h:61
+#: ../data/glade/preferences_window.glade.h:57
+msgid ""
+"If checked, Gajim will keep logs for encrypted messages. Please note that "
+"when using E2E encryption the remote party has to agree on logging, else the "
+"messages will not be logged."
+msgstr ""
+"Wenn aktiviert, wird Gajim Logs von verschlüsselten Nachrichten aufzeichnen. "
+"Bitte bedenken Sie, dass bei der Benutzung von Punkt-zu-Punkt-"
+"Verschlüsselung der Chatpartner zustimmen muss; ansonsten wird nicht "
+"mitgeschnitten."
+
+#: ../data/glade/preferences_window.glade.h:58
msgid ""
"If checked, Gajim will show a notification when a new e-mail is received via "
"GMail"
@@ -1856,7 +1872,7 @@ msgstr ""
"Wenn aktiviert, wird Gajim eine Benachrichtigung anzeigen, wenn eine neue E-"
"Mail über Googlemail empfangen wurden"
-#: ../data/glade/preferences_window.glade.h:62
+#: ../data/glade/preferences_window.glade.h:59
msgid ""
"If checked, Gajim will sort contacts in roster window and groupchats by "
"their status and not by the shown name"
@@ -1864,7 +1880,7 @@ msgstr ""
"Wenn aktiviert, wird Gajim die Kontakte in der Kontaktliste und Gruppenchats "
"nach ihrem Status und nicht nach dem angezeigten Namen sortieren"
-#: ../data/glade/preferences_window.glade.h:63
+#: ../data/glade/preferences_window.glade.h:60
msgid ""
"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, "
@@ -1874,13 +1890,15 @@ msgstr ""
"verwenden (Ein Kontakt aus MSN wird z.B. das äquivalente MSN-Symbol für den "
"Status \"Anwesend\", \"Abwesend\", \"Beschäftigt\", etc. haben)"
-#: ../data/glade/preferences_window.glade.h:64
+#: ../data/glade/preferences_window.glade.h:61
msgid ""
"If enabled, Gajim will not ask for a status message. The specified default "
"message will be used instead."
msgstr ""
+"Wenn aktiviert, wird Gajim nicht nach einer Statusnachricht fragen. Die "
+"Standardnachricht wird anstelle benutzt"
-#: ../data/glade/preferences_window.glade.h:65
+#: ../data/glade/preferences_window.glade.h:62
msgid ""
"If not disabled, Gajim will replace ascii smilies like ':)' with equivalent "
"animated or static graphical emoticons"
@@ -1888,53 +1906,43 @@ msgstr ""
"Wenn nicht deaktivert, wird Gajim ASCII-Smilies wie ':)' mit äquivalenten "
"graphischen Emoticons ersetzen"
-#: ../data/glade/preferences_window.glade.h:66
+#: ../data/glade/preferences_window.glade.h:63
msgid "Ignore rich content in incoming messages"
msgstr "Formatierten Inhalt in ankommenden Nachrichten ignorieren"
-#: ../data/glade/preferences_window.glade.h:67
+#: ../data/glade/preferences_window.glade.h:64
msgid "Log _encrypted chat session"
msgstr "_Verschlüsselte Sitzungen mitschneiden"
-#: ../data/glade/preferences_window.glade.h:68
+#: ../data/glade/preferences_window.glade.h:65
msgid "Ma_nage..."
msgstr "_Verwalten ..."
-#: ../data/glade/preferences_window.glade.h:69 ../src/roster_window.py:4480
-msgid "Mood"
-msgstr ""
-
-#. holds nickname
-#: ../data/glade/preferences_window.glade.h:70 ../src/history_manager.py:149
-#: ../src/history_manager.py:201
-msgid "Nickname"
-msgstr "Spitzname"
-
-#: ../data/glade/preferences_window.glade.h:71
+#: ../data/glade/preferences_window.glade.h:66
msgid "Notifications"
msgstr "Benachrichtigungen"
-#: ../data/glade/preferences_window.glade.h:72
+#: ../data/glade/preferences_window.glade.h:67
msgid "Notify me about contacts that sign _in"
msgstr "Ãœber Kontakte benachrichtigen, die sich einloggen"
-#: ../data/glade/preferences_window.glade.h:73
+#: ../data/glade/preferences_window.glade.h:68
msgid "Notify me about contacts that sign _out"
msgstr "Ãœber Kontakte benachrichtigen, die sich abmelden"
-#: ../data/glade/preferences_window.glade.h:74
+#: ../data/glade/preferences_window.glade.h:69
msgid "Notify on new _GMail email"
msgstr "Ãœber neue _Google Mail E-Mails benachrichtigen"
-#: ../data/glade/preferences_window.glade.h:75
+#: ../data/glade/preferences_window.glade.h:70
msgid "Personal Events"
msgstr "Persönliche Ereignisse"
-#: ../data/glade/preferences_window.glade.h:76
+#: ../data/glade/preferences_window.glade.h:71
msgid "Play _sounds"
msgstr "_Klänge abspielen"
-#: ../data/glade/preferences_window.glade.h:77
+#: ../data/glade/preferences_window.glade.h:72
msgid ""
"Pop it up\n"
"Notify me about it\n"
@@ -1944,23 +1952,19 @@ msgstr ""
"Benachrichtige mich darüber\n"
"Nur im Roster anzeigen"
-#: ../data/glade/preferences_window.glade.h:80
+#: ../data/glade/preferences_window.glade.h:75
msgid "Preferences"
msgstr "Einstellungen"
-#: ../data/glade/preferences_window.glade.h:81
-msgid "Receive your contact's:"
-msgstr "Empfang von Kontakten:"
-
-#: ../data/glade/preferences_window.glade.h:82
+#: ../data/glade/preferences_window.glade.h:76
msgid "Sign _in"
msgstr "_anmelden"
-#: ../data/glade/preferences_window.glade.h:83
+#: ../data/glade/preferences_window.glade.h:77
msgid "Sign _out"
msgstr "a_bmelden"
-#: ../data/glade/preferences_window.glade.h:84
+#: ../data/glade/preferences_window.glade.h:78
msgid ""
"Some messages may include rich content (formatting, colors etc). If checked, "
"Gajim will just display the raw message text."
@@ -1969,127 +1973,127 @@ msgstr ""
"Schriftarten etc.). Wenn aktiviert, wird Gajim nur den puren Nachrichtentext "
"anzeigen."
-#: ../data/glade/preferences_window.glade.h:85
+#: ../data/glade/preferences_window.glade.h:79 ../src/config.py:382
msgid "Status"
msgstr "Status"
-#: ../data/glade/preferences_window.glade.h:86
+#: ../data/glade/preferences_window.glade.h:80
msgid "Status _iconset:"
msgstr "Status-S_ymbole:"
-#: ../data/glade/preferences_window.glade.h:87
+#: ../data/glade/preferences_window.glade.h:81
msgid "Style"
msgstr "Stil"
-#: ../data/glade/preferences_window.glade.h:88
+#: ../data/glade/preferences_window.glade.h:82
msgid "T_heme:"
msgstr "T_hema:"
-#: ../data/glade/preferences_window.glade.h:89
+#: ../data/glade/preferences_window.glade.h:83
msgid ""
"The auto away status message. If empty, Gajim will not change the current "
"status message"
msgstr ""
+"Die automatische Away-Status-Nachricht. Wenn leer, wird Gajim die aktuelle "
+"Statusnachricht belassen"
-#: ../data/glade/preferences_window.glade.h:90
+#: ../data/glade/preferences_window.glade.h:84
msgid ""
"The auto not available status message. If empty, Gajim will not change the "
"current status message"
msgstr ""
+"Die automatische Nicht-Verfügbar-Status-Nachricht. Wenn leer, wird Gajim die "
+"aktuelle Statusnachricht belassen"
-#: ../data/glade/preferences_window.glade.h:91
-msgid "Tune"
-msgstr "Musiktitel:"
-
-#: ../data/glade/preferences_window.glade.h:92
+#: ../data/glade/preferences_window.glade.h:85
msgid "Use _transports icons"
msgstr "_Transport-Symbole verwenden"
-#: ../data/glade/preferences_window.glade.h:93
+#: ../data/glade/preferences_window.glade.h:86
msgid "Use system _default"
msgstr "Benutze System-_Voreinstellung"
-#: ../data/glade/preferences_window.glade.h:94
+#: ../data/glade/preferences_window.glade.h:87
msgid "When new event is received:"
msgstr "Wenn neue Nachricht empfangen wird"
-#: ../data/glade/preferences_window.glade.h:95
+#: ../data/glade/preferences_window.glade.h:88
msgid "_Away after:"
msgstr "Automatisch _abwesend nach:"
-#: ../data/glade/preferences_window.glade.h:96
+#: ../data/glade/preferences_window.glade.h:89
msgid "_Browser:"
msgstr "_Browser:"
-#: ../data/glade/preferences_window.glade.h:97
+#: ../data/glade/preferences_window.glade.h:90
msgid "_Display chat state notifications:"
msgstr "Zeige Benachrichtigung über Chatstatus:"
-#: ../data/glade/preferences_window.glade.h:98
+#: ../data/glade/preferences_window.glade.h:91
msgid "_File manager:"
msgstr "_Dateimanager:"
-#: ../data/glade/preferences_window.glade.h:99
+#: ../data/glade/preferences_window.glade.h:92
msgid "_Highlight misspelled words"
msgstr "Falsch geschriebene Wörter _hervorheben"
-#: ../data/glade/preferences_window.glade.h:100
+#: ../data/glade/preferences_window.glade.h:93
msgid "_Ignore events from contacts not in the roster"
msgstr "Ereignisse von Kontakten, die nicht in der Liste sind, _ignorieren"
-#: ../data/glade/preferences_window.glade.h:101
+#: ../data/glade/preferences_window.glade.h:94
msgid "_Incoming message:"
msgstr "_Eingehende Nachricht:"
-#: ../data/glade/preferences_window.glade.h:102
+#: ../data/glade/preferences_window.glade.h:95
msgid "_Log status changes of contacts"
msgstr "_Logge die Statusveränderungen von Kontakten"
-#: ../data/glade/preferences_window.glade.h:103
+#: ../data/glade/preferences_window.glade.h:96
msgid "_Mail client:"
msgstr "_Mail-Programm:"
-#: ../data/glade/preferences_window.glade.h:104
+#: ../data/glade/preferences_window.glade.h:97
msgid "_Make message windows compact"
msgstr "Nachrichtenfenster _kompakt anzeigen"
-#: ../data/glade/preferences_window.glade.h:105
+#: ../data/glade/preferences_window.glade.h:98
msgid "_Not available after:"
msgstr "Automatisch _nicht verfügbar nach:"
-#: ../data/glade/preferences_window.glade.h:106
+#: ../data/glade/preferences_window.glade.h:99
msgid "_Open..."
msgstr "Ö_ffnen ..."
-#: ../data/glade/preferences_window.glade.h:107
+#: ../data/glade/preferences_window.glade.h:100
msgid "_Outgoing message:"
msgstr "_Ausgehende Nachricht:"
-#: ../data/glade/preferences_window.glade.h:108
+#: ../data/glade/preferences_window.glade.h:101
msgid "_Reset to Default Colors"
msgstr "Auf Standard-Farben zu_rücksetzen"
-#: ../data/glade/preferences_window.glade.h:109
+#: ../data/glade/preferences_window.glade.h:102
msgid "_Send chat state notifications:"
msgstr "_Sende Benachrichtigung über Chatstatus:"
-#: ../data/glade/preferences_window.glade.h:110
+#: ../data/glade/preferences_window.glade.h:103
msgid "_Sort contacts by status"
msgstr "Kontakte nach Status _sortieren"
-#: ../data/glade/preferences_window.glade.h:111
+#: ../data/glade/preferences_window.glade.h:104
msgid "_Status message:"
msgstr "_Statusnachricht:"
-#: ../data/glade/preferences_window.glade.h:112
+#: ../data/glade/preferences_window.glade.h:105
msgid "_URL highlight:"
msgstr ""
-#: ../data/glade/preferences_window.glade.h:113
+#: ../data/glade/preferences_window.glade.h:106
msgid "_Window behavior:"
msgstr "Fensterverhalten"
-#: ../data/glade/preferences_window.glade.h:114
+#: ../data/glade/preferences_window.glade.h:107
msgid "minutes"
msgstr "Minuten"
@@ -2133,7 +2137,7 @@ msgstr "Jabber-ID:"
msgid "Order:"
msgstr "Reihenfolge:"
-#: ../data/glade/privacy_list_window.glade.h:11 ../src/dialogs.py:2500
+#: ../data/glade/privacy_list_window.glade.h:11 ../src/dialogs.py:2531
msgid "Privacy List"
msgstr "Privatliste"
@@ -2338,9 +2342,9 @@ msgstr "Konto _entfernen (in Gajim und auf dem Server)"
#. Remove group
#. Remove
#: ../data/glade/remove_account_window.glade.h:4
-#: ../data/glade/roster_contact_context_menu.glade.h:19
-#: ../src/roster_window.py:4754 ../src/roster_window.py:5192
-#: ../src/roster_window.py:5320
+#: ../data/glade/roster_contact_context_menu.glade.h:18
+#: ../src/roster_window.py:4836 ../src/roster_window.py:5274
+#: ../src/roster_window.py:5403
msgid "_Remove"
msgstr "_Entfernen"
@@ -2362,74 +2366,70 @@ msgstr "_Gruppen bearbeiten ..."
#. Execute Command
#: ../data/glade/roster_contact_context_menu.glade.h:5
-#: ../src/roster_window.py:5263
+#: ../src/roster_window.py:5346
msgid "Execute Command..."
msgstr "_Befehl ausführen ..."
#. Invite to
#. Invite to Groupchat
#: ../data/glade/roster_contact_context_menu.glade.h:6
-#: ../src/roster_window.py:4675 ../src/roster_window.py:5145
+#: ../src/roster_window.py:4755 ../src/roster_window.py:5227
msgid "In_vite to"
msgstr "_Einladen zu"
-#: ../data/glade/roster_contact_context_menu.glade.h:7
-msgid "Play Tic Tac Toe"
-msgstr ""
-
#. Send Custom Status
-#: ../data/glade/roster_contact_context_menu.glade.h:8
-#: ../src/roster_window.py:4684 ../src/roster_window.py:5230
+#: ../data/glade/roster_contact_context_menu.glade.h:7
+#: ../src/roster_window.py:4765 ../src/roster_window.py:5312
msgid "Send Cus_tom Status"
msgstr "Benutzerdefinierten Status senden"
-#: ../data/glade/roster_contact_context_menu.glade.h:9
+#: ../data/glade/roster_contact_context_menu.glade.h:8
msgid "Send Single _Message..."
msgstr "Einzelne _Nachricht senden ..."
-#: ../data/glade/roster_contact_context_menu.glade.h:10
+#: ../data/glade/roster_contact_context_menu.glade.h:9
msgid "Send _File..."
msgstr "_Datei senden ..."
-#: ../data/glade/roster_contact_context_menu.glade.h:11
+#: ../data/glade/roster_contact_context_menu.glade.h:10
msgid "Set Custom _Avatar..."
msgstr "Benutzerdefinierten Avatar wählen ..."
-#: ../data/glade/roster_contact_context_menu.glade.h:12
+#: ../data/glade/roster_contact_context_menu.glade.h:11
#: ../data/glade/zeroconf_contact_context_menu.glade.h:5
msgid "Start _Chat"
msgstr "_Chat starten"
-#: ../data/glade/roster_contact_context_menu.glade.h:14
+#: ../data/glade/roster_contact_context_menu.glade.h:13
msgid "_Allow him/her to see my status"
msgstr "_Erlaube Kontakt, meinen Status zu sehen"
-#: ../data/glade/roster_contact_context_menu.glade.h:15
-#: ../src/roster_window.py:4745 ../src/roster_window.py:5182
-#: ../src/roster_window.py:5310
+#: ../data/glade/roster_contact_context_menu.glade.h:14
+#: ../src/roster_window.py:4827 ../src/roster_window.py:5264
+#: ../src/roster_window.py:5393
msgid "_Block"
msgstr "_Blockieren"
-#: ../data/glade/roster_contact_context_menu.glade.h:16
+#: ../data/glade/roster_contact_context_menu.glade.h:15
msgid "_Forbid him/her to see my status"
msgstr "_Verbiete Kontakt, meinen Status zu sehen"
-#: ../data/glade/roster_contact_context_menu.glade.h:18
+#: ../data/glade/roster_contact_context_menu.glade.h:17
#: ../data/glade/zeroconf_contact_context_menu.glade.h:7
msgid "_Manage Contact"
msgstr "Kontakt verwalten"
-#: ../data/glade/roster_contact_context_menu.glade.h:20
+#: ../data/glade/roster_contact_context_menu.glade.h:19
msgid "_Rename..."
msgstr "_Umbenennen ..."
-#: ../data/glade/roster_contact_context_menu.glade.h:21
+#: ../data/glade/roster_contact_context_menu.glade.h:20
msgid "_Subscription"
msgstr "_Abonnement"
-#: ../data/glade/roster_contact_context_menu.glade.h:22
-#: ../src/roster_window.py:4739 ../src/roster_window.py:5176
-#: ../src/roster_window.py:5307
+#: ../data/glade/roster_contact_context_menu.glade.h:21
+#: ../src/roster_window.py:4821 ../src/roster_window.py:5258
+#: ../src/roster_window.py:5390
msgid "_Unblock"
msgstr "Entblocken"
@@ -2524,7 +2524,7 @@ msgid "_Add contact"
msgstr "_Kontakt hinzufügen"
#. Information
-#: ../data/glade/search_window.glade.h:4 ../src/roster_window.py:5332
+#: ../data/glade/search_window.glade.h:4 ../src/roster_window.py:5415
msgid "_Information"
msgstr "Informationen"
@@ -2616,6 +2616,10 @@ msgstr "Abonnementanfrage"
msgid "_Deny"
msgstr "_Ablehnen"
+#: ../data/glade/synchronise_contacts_dialog.glade.h:1
+msgid "Select the account with which to synchronise"
+msgstr "Wählen Sie das Benutzerkonto mit welchem synchronisiert werden soll"
+
#: ../data/glade/synchronise_select_account_dialog.glade.h:1
msgid "Select the account with which you want to synchronise"
msgstr "Das Konto auswählen, mit dem Sie synchronisieren möchten"
@@ -2743,13 +2747,13 @@ msgstr "Open_PGP-Schlüssel zuweisen"
#. Edit Groups
#: ../data/glade/zeroconf_contact_context_menu.glade.h:3
-#: ../src/roster_window.py:5165
+#: ../src/roster_window.py:5247
msgid "Edit _Groups"
msgstr "_Gruppen bearbeiten"
#. Rename
#: ../data/glade/zeroconf_contact_context_menu.glade.h:8
-#: ../src/roster_window.py:5290
+#: ../src/roster_window.py:5373
msgid "_Rename"
msgstr "_Umbenennen"
@@ -2966,56 +2970,55 @@ msgstr "Ihre Nachricht kann erst gesendet werden, wenn Sie verbunden sind."
#. Add to roster button
#. add_to_roster_menuitem
-#: ../src/chat_control.py:1065 ../src/chat_control.py:1797
-#: ../src/conversation_textview.py:724 ../src/dialogs.py:835
-#: ../src/gajim.py:1016 ../src/gajim.py:1017 ../src/gajim.py:1564
-#: ../src/gajim.py:1700 ../src/roster_window.py:758
-#: ../src/roster_window.py:768 ../src/roster_window.py:855
-#: ../src/roster_window.py:1386 ../src/roster_window.py:1388
-#: ../src/roster_window.py:1716 ../src/roster_window.py:2976
-#: ../src/roster_window.py:4857 ../src/roster_window.py:5031
+#: ../src/chat_control.py:1065 ../src/chat_control.py:1855
#: ../src/common/contacts.py:94 ../src/common/helpers.py:66
-#: ../src/common/helpers.py:280
+#: ../src/common/helpers.py:280 ../src/conversation_textview.py:724
+#: ../src/dialogs.py:864 ../src/gajim.py:1036 ../src/gajim.py:1037
+#: ../src/gajim.py:1706 ../src/roster_window.py:759
+#: ../src/roster_window.py:770 ../src/roster_window.py:857
+#: ../src/roster_window.py:1417 ../src/roster_window.py:1419
+#: ../src/roster_window.py:1772 ../src/roster_window.py:3026
+#: ../src/roster_window.py:4939 ../src/roster_window.py:5112
msgid "Not in Roster"
msgstr "Nicht in der Kontaktliste"
-#: ../src/chat_control.py:1126 ../src/chat_control.py:1373
+#: ../src/chat_control.py:1128 ../src/chat_control.py:1395
msgid "GPG encryption enabled"
msgstr "Verschlüsselung aktiviert"
-#: ../src/chat_control.py:1283
+#: ../src/chat_control.py:1305
#, python-format
msgid "%(nickname)s from group chat %(room_name)s"
msgstr "%(nickname)s aus Gruppenchat %(room_name)s"
-#: ../src/chat_control.py:1364
+#: ../src/chat_control.py:1386
msgid "GPG encryption disabled"
msgstr "Verschlüsselung deaktiviert"
-#: ../src/chat_control.py:1379 ../src/chat_control.py:1596
+#: ../src/chat_control.py:1401 ../src/chat_control.py:1629
msgid "Session WILL NOT be logged"
msgstr "Sitzung wird nicht aufgezeichnet"
-#: ../src/chat_control.py:1381 ../src/chat_control.py:1594
+#: ../src/chat_control.py:1403 ../src/chat_control.py:1627
msgid "Session WILL be logged"
msgstr "Sitzung wird aufgezeichnet"
-#: ../src/chat_control.py:1445 ../src/groupchat_control.py:1492
+#: ../src/chat_control.py:1478 ../src/groupchat_control.py:1524
#, python-format
msgid "Commands: %s"
msgstr "Befehle: %s"
-#: ../src/chat_control.py:1448 ../src/groupchat_control.py:1506
+#: ../src/chat_control.py:1481 ../src/groupchat_control.py:1538
#, python-format
msgid "Usage: /%s, clears the text window."
msgstr "Bedienung: /%s, leert das Textfenster."
-#: ../src/chat_control.py:1451 ../src/groupchat_control.py:1511
+#: ../src/chat_control.py:1484 ../src/groupchat_control.py:1543
#, python-format
msgid "Usage: /%s, hide the chat buttons."
msgstr "Verwendung: /%s, versteckt die Chatbuttons."
-#: ../src/chat_control.py:1454 ../src/groupchat_control.py:1527
+#: ../src/chat_control.py:1487 ../src/groupchat_control.py:1559
#, python-format
msgid ""
"Usage: /%s <action>, sends action to the current group chat. Use third "
@@ -3024,48 +3027,46 @@ msgstr ""
"Bedienung: /%s <Aktion>, sendet die Aktion im aktuellen Gruppenchat. "
"Verwende dritte Person, z.B. /%s explodiert)."
-#: ../src/chat_control.py:1458
+#: ../src/chat_control.py:1491
#, python-format
msgid "Usage: /%s, sends a ping to the contact"
msgstr "Bedienung: /%s, sendet einen Ping zum Kontakt"
-#: ../src/chat_control.py:1461
+#: ../src/chat_control.py:1494
#, python-format
msgid "Usage: /%s, send the message to the contact"
msgstr "Verwendung: /%s, sendet eine Nachricht zum Kontakt"
-#: ../src/chat_control.py:1464 ../src/groupchat_control.py:1550
+#: ../src/chat_control.py:1497 ../src/groupchat_control.py:1582
#, python-format
msgid "No help info for /%s"
msgstr "Keine Hilfe vorhanden für /%s"
-#: ../src/chat_control.py:1583
+#: ../src/chat_control.py:1616
msgid "Session negotiation cancelled"
msgstr "Sitzungs-Aushandlung abgebrochen"
-#: ../src/chat_control.py:1590
+#: ../src/chat_control.py:1623
msgid "E2E encryption enabled"
msgstr "Verschlüsselung aktiviert"
-#: ../src/chat_control.py:1600
+#: ../src/chat_control.py:1633
msgid "E2E encryption disabled"
msgstr "Verschlüsselung deaktiviert"
-#: ../src/chat_control.py:1626 ../src/chat_control.py:1637
-msgid "The following message was NOT encrypted"
-msgstr "[Die folgende Nachricht wurde nicht verschlüsselt]"
-
-#: ../src/chat_control.py:1632
-msgid "The following message was encrypted"
+#: ../src/chat_control.py:1661 ../src/chat_control.py:1670
+#: ../src/chat_control.py:1677 ../src/chat_control.py:1683
+#, fuzzy
+msgid "The following message was "
msgstr "[Die folgende Nachricht wurde verschlüsselt]"
#. %s is being replaced in the code with JID
-#: ../src/chat_control.py:1998
+#: ../src/chat_control.py:2057
#, python-format
msgid "You just received a new message from \"%s\""
msgstr "Sie haben eine neue Nachricht von \"%s\""
-#: ../src/chat_control.py:1999
+#: ../src/chat_control.py:2058
msgid ""
"If you close this tab and you have history disabled, this message will be "
"lost."
@@ -3073,43 +3074,2399 @@ msgstr ""
"Wenn sie das Fenster schließen und der Verlauf abgeschaltet ist, geht die "
"Nachricht verloren."
-#: ../src/chat_control.py:2131 ../src/gajim.py:158
+#: ../src/chat_control.py:2190 ../src/gajim.py:159
msgid "Database Error"
msgstr "Datenbankfehler"
-#: ../src/chat_control.py:2132 ../src/gajim.py:159
+#: ../src/chat_control.py:2191 ../src/gajim.py:160
#, python-format
msgid ""
"The database file (%s) cannot be read. Try to repare it or remove it (all "
"history will be lost)."
msgstr ""
+"Die Datenbankdatei (%s) konnte nicht gelesen werden. Versuchen Sie diese zu "
+"reparieren oder zu löschen (dabei werden alle Verläufe verloren gehen)."
+
+#: ../src/chat_control.py:2419
+#, python-format
+msgid "Private conversation with %s lost."
+msgstr "Private Unterhaltung mit %s unterbrochen."
+
+#: ../src/common/check_paths.py:38
+msgid "creating logs database"
+msgstr "Erstelle Log-Datenbank"
+
+#: ../src/common/check_paths.py:105 ../src/common/check_paths.py:116
+#: ../src/common/check_paths.py:123
+#, python-format
+msgid "%s is a file but it should be a directory"
+msgstr "%s ist eine Datei, aber es sollte ein Verzeichnis sein"
+
+#: ../src/common/check_paths.py:106 ../src/common/check_paths.py:117
+#: ../src/common/check_paths.py:124 ../src/common/check_paths.py:132
+msgid "Gajim will now exit"
+msgstr "Gajim wird nun beendet"
+
+#: ../src/common/check_paths.py:131
+#, python-format
+msgid "%s is a directory but should be a file"
+msgstr "%s ist ein Verzeichnis, sollte aber eine Datei sein"
+
+#: ../src/common/check_paths.py:147
+#, python-format
+msgid "creating %s directory"
+msgstr "Erstelle Verzeichnis %s"
+
+#: ../src/common/commands.py:74
+msgid "Change status information"
+msgstr "Ändere Statusinformation"
+
+#: ../src/common/commands.py:87
+msgid "Change status"
+msgstr "Kontakt hat Status verändert"
+
+#: ../src/common/commands.py:88
+msgid "Set the presence type and description"
+msgstr "Lege den Anwesenheitstyp und die Beschreibung fest"
+
+#: ../src/common/commands.py:94
+msgid "Free for chat"
+msgstr "Frei zum Chatten"
+
+#: ../src/common/commands.py:95
+msgid "Online"
+msgstr "Online"
+
+#: ../src/common/commands.py:97
+msgid "Extended away"
+msgstr "Längere Zeit abwesend"
+
+#: ../src/common/commands.py:98
+msgid "Do not disturb"
+msgstr "Bitte nicht stören"
+
+#: ../src/common/commands.py:99
+msgid "Offline - disconnect"
+msgstr "Offline - Abgemeldet"
+
+#: ../src/common/commands.py:104
+msgid "Presence description:"
+msgstr "Anwesenheitsbeschreibung:"
+
+#: ../src/common/commands.py:139
+msgid "The status has been changed."
+msgstr "Der Status hat sich verändert."
+
+#: ../src/common/commands.py:170 ../src/common/commands.py:194
+msgid "Leave Groupchats"
+msgstr "Verlasse Gruppenchat"
+
+#: ../src/common/commands.py:184
+#, python-format
+msgid "%(nickname)s on %(room_jid)s"
+msgstr "%(nickname)s aus Gruppenchat %(room_jid)s"
+
+#: ../src/common/commands.py:188
+msgid "You have not joined a groupchat."
+msgstr "Sie haben keinen Gruppenchat betreten."
+
+#: ../src/common/commands.py:195
+msgid "Choose the groupchats you want to leave"
+msgstr "Wählen Sie den Gruppenchat, den Sie verlassen möchten"
+
+#. Make special context menu if group is Groupchats
+#: ../src/common/commands.py:199 ../src/common/contacts.py:107
+#: ../src/common/helpers.py:66 ../src/roster_window.py:714
+#: ../src/roster_window.py:1421 ../src/roster_window.py:1423
+#: ../src/roster_window.py:2738 ../src/roster_window.py:4725
+msgid "Groupchats"
+msgstr "Gruppenchat"
+
+#: ../src/common/commands.py:235
+msgid "You left the following groupchats:"
+msgstr "Sie haben folgende Gruppenchats verlassen:"
+
+#: ../src/common/commands.py:247
+msgid "Forward unread messages"
+msgstr "Ungelesene Nachrichten weiterleiten"
+
+#: ../src/common/commands.py:267
+msgid "All unread messages have been forwarded."
+msgstr "Alle ungelesenen Nachrichten wurden weitergeleitet."
+
+#: ../src/common/config.py:74
+msgid "Use D-Bus and Notification-Daemon to show notifications"
+msgstr ""
+"Verwende D-Bus und Notification-Daemon, um Benachrichtigungen zu zeigen"
+
+#: ../src/common/config.py:79
+msgid "Time in minutes, after which your status changes to away."
+msgstr "Zeit in Minuten, nach der Ihr Status auf abwesend gesetzt wird"
+
+#: ../src/common/config.py:80
+msgid "$S (Away as a result of being idle more than $T min)"
+msgstr "$S (Abwesend wegen Untätigkeit für mehr als $T Minuten)"
+
+#: ../src/common/config.py:80
+msgid "$S will be replaced by current status message, $T by autoaway time."
+msgstr ""
+"$S wird mit der aktuellen Statusnachricht ersetzt, $T durch die "
+"Abwesenheitszeit."
+
+#: ../src/common/config.py:82
+msgid "Time in minutes, after which your status changes to not available."
+msgstr "Zeit in Minuten, nach der Ihr Status auf nicht verfügbar gesetzt wird."
+
+#: ../src/common/config.py:83
+msgid "$S (Not available as a result of being idle more than $T min)"
+msgstr "$S (Nicht verfügbar wegen Untätigkeit seit mehr als $T Minuten)"
+
+#: ../src/common/config.py:83
+msgid "$S will be replaced by current status message, $T by autoxa time."
+msgstr ""
+"$S wird mit der aktuellen Statusnachricht ersetzt, $T durch die Nicht-"
+"Verfügbar-Zeit."
+
+#: ../src/common/config.py:101
+msgid ""
+"List (space separated) of rows (accounts and groups) that are collapsed."
+msgstr ""
+"Liste (leerzeichengeteilt) von Reihen (Kontos und Gruppen), die eingeklappt "
+"sind."
+
+#. sorted alphanum
+#: ../src/common/config.py:102 ../src/common/config.py:430
+#: ../src/common/optparser.py:212 ../src/common/optparser.py:430
+#: ../src/common/optparser.py:464 ../src/gajim.py:3237
+msgid "default"
+msgstr "Standard"
+
+#: ../src/common/config.py:105
+msgid "Enable link-local/zeroconf messaging"
+msgstr "Aktiviere Anzeige von Kontakten im LAN über Zeroconf."
+
+#: ../src/common/config.py:108
+msgid "Language used by speller"
+msgstr "Sprache der Rechtschreibprüfung"
+
+#: ../src/common/config.py:109
+msgid ""
+"'always' - print time for every message.\n"
+"'sometimes' - print time every print_ichat_every_foo_minutes minute.\n"
+"'never' - never print time."
+msgstr ""
+"'always' - Zeige Zeit für jede Nachricht.\n"
+"'sometimes' - Zeige Zeit alle print_ichat_every_foo_minutes Minuten.\n"
+"'never' - Zeige nie die Zeit."
+
+#: ../src/common/config.py:110
+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 least precise one. "
+"This is used only if print_time is 'sometimes'."
+msgstr ""
+"Zeige die Zeit in Chats mittels ungenauer Uhr an. Werte für die "
+"Ungenauigkeit sind 1 bis 4 oder 0, um die ungenaue Uhr zu deaktivieren. 1 "
+"ist am Genauesten, 4 ist am Ungenaueste. Dies wird nur verwendet, wenn "
+"print_time auf 'sometimes' gesetzt ist."
+
+#: ../src/common/config.py:113
+msgid "Treat * / _ pairs as possible formatting characters."
+msgstr "Behandle * / _ Paare als mögliche Formatierungszeichen."
+
+#: ../src/common/config.py:114
+msgid ""
+"If True, do not remove */_ . So *abc* will be bold but with * * not removed."
+msgstr ""
+"Wenn aktiviert, wird */_ nicht entfernt. So wird *abc* fett gedruckt, aber * "
+"* wird nicht entfernt."
+
+#: ../src/common/config.py:117
+msgid ""
+"Uses ReStructured text markup to send HTML, plus ascii formatting if "
+"selected. For syntax, see http://docutils.sourceforge.net/docs/ref/rst/"
+"restructuredtext.html (If you want to use this, install docutils)"
+msgstr ""
+"Wenn aktiviert, wird reStructuredText für die HTML und ASCII-Formatierung "
+"verwendet (falls dies erwünscht ist, installieren Sie docutils). "
+"Informationen zur Syntax finden Sie unter http://docutils.sourceforge.net/"
+"docs/ref/rst/restructuredtext.html (Englisch)"
+
+#: ../src/common/config.py:126
+msgid ""
+"Character to add after nickname when using nick completion (tab) in group "
+"chat."
+msgstr ""
+"Zeichen, das hinter Spitznamen angezeigt wird, wenn "
+"Spitznamenvervollständigung (Tab) im Gruppenchat verwendet wird."
+
+#: ../src/common/config.py:127
+msgid ""
+"Character to propose to add after desired nickname when desired nickname is "
+"used by someone else in group chat."
+msgstr ""
+"Zeichen, das hinter den gewünschten Spitznamen gehängt wird, falls der "
+"gewünschteName im Gruppenchat bereits vergeben ist."
+
+#: ../src/common/config.py:150
+msgid ""
+"This option let you customize timestamp that is printed in conversation. For "
+"exemple \"[%H:%M] \" will show \"[hour:minute] \". See python doc on "
+"strftime for full documentation: http://docs.python.org/lib/module-time.html"
+msgstr ""
+"Hiermit lässt sich der Zeitstempel anpassen, der in den Unterhaltungen "
+"verwendet wird. Zum Beispiel wird \"[%H:%M] \" zu \"[Stunde:Minute] \". "
+"Weitere Informationen finden Sie in der Python-Dokumentation zu 'strftime' "
+"unter http://docs.python.org/lib/module-time.html (Englisch)."
+
+#: ../src/common/config.py:151
+msgid "Characters that are printed before the nickname in conversations"
+msgstr "Zeichen, die vor dem Spitzname gedruckt werden"
+
+#: ../src/common/config.py:152
+msgid "Characters that are printed after the nickname in conversations"
+msgstr "Zeichen, die nach dem Spitznamen gedruckt werden"
+
+#: ../src/common/config.py:154
+msgid "The username used to identify the Last.fm account."
+msgstr "Benutzername zur Identifikation des Last.FM Kontos."
+
+#: ../src/common/config.py:158
+msgid "Add * and [n] in roster title?"
+msgstr "Füge * und [n] in die Kontaktleiste?"
+
+#: ../src/common/config.py:159
+msgid ""
+"How many lines to remember from previous conversation when a chat tab/window "
+"is reopened."
+msgstr ""
+"Wie viele Zeilen von der vorherigen Unterhaltung gespeichert werden, wenn "
+"ein Chat Tab/Fenster wieder geöffnet wird."
+
+#: ../src/common/config.py:160
+msgid "How many minutes should last lines from previous conversation last."
+msgstr ""
+"Wie viele Minuten die Zeilen vom vorherigen Chat angezeigt werden sollen."
+
+#: ../src/common/config.py:161
+msgid ""
+"Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ "
+"Client default behaviour)."
+msgstr ""
+"Sende Nachricht mit Strg+Enter und mache bei Enter einen Zeilenumbruch "
+"(Mirabilis ICQ-Client Standardeinstellung)."
+
+#: ../src/common/config.py:163
+msgid "How many lines to store for Ctrl+KeyUP."
+msgstr "Wie viele Zeilen für Strg+BildAuf gespeichert werden."
+
+#: ../src/common/config.py:166
+#, python-format
+msgid ""
+"Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' "
+"which means use wiktionary."
+msgstr ""
+"Entweder eine spezielle URL mit %s, wobei %s das Word/Phrase ist oder "
+"'WIKTIONARY', was bedeutet, dass das Wörterbuch Wiktionary verwendet wird."
+
+#: ../src/common/config.py:169
+msgid "If checked, Gajim can be controlled remotely using gajim-remote."
+msgstr ""
+"Falls aktiviert, kann Gajim von Außerhalb mittels gajim-remote kontrolliert "
+"werden."
+
+#: ../src/common/config.py:170
+msgid ""
+"If True, listen to D-Bus signals from NetworkManager and change the status "
+"of accounts (provided they do not have listen_to_network_manager set to "
+"False and they sync with global status) based upon the status of the network "
+"connection."
+msgstr ""
+"Wenn aktiviert, reagiere auf D-Bus-Signale vom NetworkManager und ändere den "
+"Status von Konten (vorausgesetzt, sie haben listen_to_network_manager nicht "
+"auf False gesetzt und sie synchronisieren sich mit dem globalen Status) "
+"basierend auf dem Status der Netzwerkverbindung."
+
+#: ../src/common/config.py:171
+msgid ""
+"Sent chat state notifications. Can be one of all, composing_only, disabled."
+msgstr ""
+"Chat-Statusbenachrichtigungen gesendet. Kann eine von allen sein, "
+"composing_only deaktiviert."
+
+#: ../src/common/config.py:172
+msgid ""
+"Displayed chat state notifications in chat windows. Can be one of all, "
+"composing_only, disabled."
+msgstr ""
+"Angezeigte Chat-Status Benachrichtigungen im Chatfenster. Kann all, "
+"composing_only oder disabled sein."
+
+#: ../src/common/config.py:174
+msgid ""
+"When not printing time for every message (print_time==sometimes), print it "
+"every x minutes."
+msgstr ""
+"Wenn die Uhrzeit nicht für jede Nachricht angezeigt werden soll "
+"(print_time==sometimes), zeige sie alle x Minuten."
+
+#: ../src/common/config.py:175
+msgid "Ask before closing a group chat tab/window."
+msgstr "Fragen, bevor ein Gruppenchat Tab/Fenster geschlossen wird."
+
+#: ../src/common/config.py:176
+msgid ""
+"Always ask before closing group chat tab/window in this space separated list "
+"of group chat jids."
+msgstr ""
+"Immer fragen, bevor ein Gruppenchat-Fenster/Tab aus der Leerzeichen-"
+"seperierten Liste von Gruppchatnamen geschlossen wird."
+
+#: ../src/common/config.py:177
+msgid ""
+"Never ask before closing group chat tab/window in this space separated list "
+"of group chat jids."
+msgstr ""
+"Niemals fragen, bevor ein Gruppenchat-Fenster/Tab aus der Leerzeichen-"
+"seperierten Liste von Gruppenchatnamen geschlossen wird."
+
+#: ../src/common/config.py:180
+#, fuzzy
+msgid ""
+"Comma separated list of hosts that we send, in addition of local interfaces, "
+"for File Transfer in case of address translation/port forwarding."
+msgstr ""
+"Überschreibt den Host für Datenübertragung für den Fall von NAT/Port-"
+"Forwarding"
+
+#: ../src/common/config.py:182
+msgid "IEC standard says KiB = 1024 bytes, KB = 1000 bytes."
+msgstr "IEC-Standard sagt KiB = 1024 Byte, kB = 1000 Byte."
+
+#: ../src/common/config.py:184
+msgid "Notify of events in the system trayicon."
+msgstr "Benachrichtige über Ereignisse durch das Trayicon"
+
+#: ../src/common/config.py:190
+msgid "Show tab when only one conversation?"
+msgstr "Tab bei einer einzelnen Unterhaltung anzeigen?"
+
+#: ../src/common/config.py:191
+msgid "Show tabbed notebook border in chat windows?"
+msgstr "Tab-Grenze im Chat-Fenster anzeigen?"
+
+#: ../src/common/config.py:192
+msgid "Show close button in tab?"
+msgstr "Schließen-Button im Tab anzeigen?"
+
+#: ../src/common/config.py:193
+msgid ""
+"When negotiating an encrypted session, should Gajim assume you want your "
+"messages to be logged?"
+msgstr ""
+"Soll Gajim beim Aushandeln einer verschlüsselten Sitzung annehmen, dass Sie "
+"Ihre Nachricht mitschneiden wollen?"
+
+#: ../src/common/config.py:194
+msgid ""
+"When negotiating an encrypted session, should Gajim prefer to use public "
+"keys for identification?"
+msgstr ""
+"Soll Gajim bei Aushandeln einer verschlüsselten Situng öffentliche Schlüssel "
+"zur Identifikation bevorzugen?"
+
+#: ../src/common/config.py:203
+msgid "Preview new messages in notification popup?"
+msgstr "Vorschau neuer Nachrichten im Benachrichtigungs-Popup?"
+
+#: ../src/common/config.py:208
+msgid ""
+"A semicolon-separated list of words that will be highlighted in group chats."
+msgstr ""
+"Eine mit Semilkolon seperierte Liste von Worten, die in einem Gruppenchat "
+"hervorgehoben werden."
+
+#: ../src/common/config.py:209
+msgid ""
+"If True, quits Gajim when X button of Window Manager is clicked. This "
+"setting is taken into account only if trayicon is used."
+msgstr ""
+"Falls aktiviert, wird Gajim beendet, wenn der X-Button des Fenstermanagers "
+"geklickt wird. Diese Option wird nur beachtet, wenn die Trayicon-"
+"Unterstützung aktiv ist."
+
+#: ../src/common/config.py:210
+msgid ""
+"If True, Gajim will check if it's the default jabber client on each startup."
+msgstr ""
+"Wenn aktiviert, wird Gajim beim Starten überprüfen, ob Gajim der Standard-"
+"Jabber-Client ist."
+
+#: ../src/common/config.py:211
+msgid ""
+"If True, Gajim will display an icon on each tab containing unread messages. "
+"Depending on the theme, this icon may be animated."
+msgstr ""
+"Falls aktiviert, wird Gajim ein Icon auf jedem Tab mit ungelesenen "
+"Nachrichten anzeigen. Abhängig vom Thema kann dieses Icon animiert sein."
+
+#: ../src/common/config.py:212
+msgid ""
+"If True, Gajim will display the status message, if not empty, for every "
+"contact under the contact name in roster window."
+msgstr ""
+"Falls aktiviert, wird Gajim für jeden Kontakt die Statusnachricht, falls "
+"nicht leer, unter dem jeweiligen Kontaktnamen im Roster anzeigen."
+
+#: ../src/common/config.py:214
+msgid "Define the position of the avatar in roster. Can be left or right"
+msgstr ""
+"Definiere die Position des Avatars in der Kontaktliste. Links oder rechts "
+"sind mögliche Optionen."
+
+#: ../src/common/config.py:215
+msgid ""
+"If True, Gajim will ask for avatar each contact that did not have an avatar "
+"last time or has one cached that is too old."
+msgstr ""
+"Falls aktiviert fragt Gajim nach einem Avatar für jeden Kontakt, der beim "
+"letzten Mal keinen hatte oder einen veralteten hat."
+
+#: ../src/common/config.py:216
+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 ""
+"Falls deaktiviert, werden Sie keine Statuszeile im Chat sehen, wenn ein "
+"Kontakt seinen Status und/oder seine Statusnachricht ändert."
+
+#: ../src/common/config.py:217
+msgid ""
+"can be \"none\", \"all\" or \"in_and_out\". If \"none\", Gajim will no "
+"longer print status line in groupchats when a member changes his or her "
+"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 ""
+"Kann \"none\", \"all\" oder \"in_and_out\" sein. Falls \"none\", wird Gajim "
+"keine Status-Zeilen mehr in Gruppenchats anzeigen, wenn ein Mitglied seinen "
+"Status oder seine Statusnachricht ändert. Falls \"all\", wird Gajim alle "
+"Statusnachrichten anzeigen. Falls \"in_and_out\", wird Gajim nur einen "
+"Status anzeigen, wenn jemand den Gruppenchat betritt oder verlässt."
+
+#: ../src/common/config.py:219
+msgid "Background color of contacts when they just signed in."
+msgstr "Hintergrundfarbe von Kontakten wenn sie sich gerade noch anmeldeten."
+
+#: ../src/common/config.py:220
+msgid "Background color of contacts when they just signed out."
+msgstr "Hintergrundfarbe von Kontakten wenn sie sich gerade noch abmeldeten."
+
+#: ../src/common/config.py:222
+msgid ""
+"If True, restored messages will use a smaller font than the default one."
+msgstr ""
+"Falls aktiv, werden wiederhergestellte Nachrichten mit einer kleineren "
+"Schrift als der Standard dargestellt."
+
+#: ../src/common/config.py:223
+msgid "Don't show avatar for the transport itself."
+msgstr "Zeige keinen Avatar für den Transport selber."
+
+#: ../src/common/config.py:224
+msgid "Don't show roster in the system taskbar."
+msgstr "Kontaktliste nicht in der System-Taskleiste anzeigen."
+
+#: ../src/common/config.py:225
+msgid ""
+"If True and installed GTK+ and PyGTK versions are at least 2.8, make the "
+"window flash (the default behaviour in most Window Managers) when holding "
+"pending events."
+msgstr ""
+"Wenn aktiviert und die installierten GTK+ und PyGTK Versionen wenigstens 2.8 "
+"sind, lasse das Fenster bei neuen Ereignissen aufblitzen (Standardverhalten "
+"in den meisten Window-Managers) "
+
+#: ../src/common/config.py:227
+msgid ""
+"Jabberd1.4 does not like sha info when one join a password protected group "
+"chat. Turn this option to False to stop sending sha info in group chat "
+"presences."
+msgstr ""
+"Jabberd1.4 mag keine SHA-Informationen wenn ein passwortgeschützter "
+"Gruppenchat betreten wird. Deaktivieren Sie diese Option um keine SHA-"
+"Informationen in Gruppenchats zu senden"
+
+#. always, never, peracct, pertype should not be translated
+#: ../src/common/config.py:230
+msgid ""
+"Controls the window where new messages are placed.\n"
+"'always' - All messages are sent to a single window.\n"
+"'always_with_roster' - Like 'always' but the messages are in a single window "
+"along with the roster.\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."
+msgstr ""
+"Kontrolliert das Fenster in dem neue Nachrichten platziert werden.\n"
+"'Immer' - Alle Nachrichten landen in einem einzelnen Fenster.\n"
+"'Immer im Kontaktfenster' - Wie 'Immer', abe rdie Nachrichten sind in einem "
+"einzelnen Fenster zusammen mit der Kontaktliste.\n"
+"'Niemals' - Alle Nachrichten bekommen ihr eigenes Fenster.\n"
+"'Pro Konto' - Nachrichten für jedes Konto landen in einem speziellen "
+"Fenster.\n"
+"'Pro Typ' - Jede Nachrichtentyp (z.B. Chat vs. Gruppenchat) landet in einem "
+"speziellen Fenster. Falls Sie diese Option ändern, muss Gajim neu gestartet "
+"werden, damit die Änderungen übernommen werden"
+
+#: ../src/common/config.py:231
+msgid "If False, you will no longer see the avatar in the chat window."
+msgstr ""
+"Wenn deaktiviert, werden Sie den Avatar nicht mehr im Chatfenster sehen."
+
+#: ../src/common/config.py:232
+msgid "If True, pressing the escape key closes a tab/window."
+msgstr "Wenn aktiviert, schließt die Escape-Taste ein Tab/Fenster."
+
+#: ../src/common/config.py:233
+msgid "Hides the buttons in chat windows."
+msgstr "Versteckt die Knöpfe im Gruppenchat-Fenster."
+
+#: ../src/common/config.py:234
+msgid "Hides the banner in a group chat window"
+msgstr "Versteckt das Banner im Gruppenchat-Fenster"
+
+#: ../src/common/config.py:235
+msgid "Hides the banner in two persons chat window"
+msgstr "Versteckt das Banner im Zwei-Personen-Chatfenster"
+
+#: ../src/common/config.py:236
+msgid "Hides the group chat occupants list in group chat window."
+msgstr "Versteckt die Gruppenchat-Benutzerliste im Gruppenchat-Fenster."
+
+#: ../src/common/config.py:237
+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 ""
+"Zeige in einem Chat den Spitznamen nur dann am Beginn einer Linie, wenn es "
+"nicht die selbe Person ist die schon die letzte Nachricht geschrieben hat."
+
+#: ../src/common/config.py:238
+msgid "Indentation when using merge consecutive nickname."
+msgstr ""
+"Einrückung, wenn das Zusammenführen nachfolgender Spitznamen verwendet wird."
+
+#: ../src/common/config.py:239
+msgid "Smooth scroll message in conversation window"
+msgstr "Sanftes Scrollen der Nachricht im Unterhaltungsfenster"
+
+#: ../src/common/config.py:240
+msgid "List of colors that will be used to color nicknames in group chats."
+msgstr ""
+"Farbliste, die für die Einfärbung von Spitzenamen im Gruppenchat verwendet "
+"wird."
+
+#: ../src/common/config.py:241
+msgid "Ctrl-Tab go to next composing tab when none is unread."
+msgstr ""
+"Strg-Tab, um zum nächsten Reiter zu gehen, wenn kein Anderer neue "
+"Nachrichten enthält."
+
+#: ../src/common/config.py:242
+msgid ""
+"Should we show the confirm metacontacts creation dialog or not? Empty string "
+"means we never show the dialog."
+msgstr ""
+"Soll der Bestätigungsdialog beim Erstellen von Metakontakten angezeigt "
+"werden oder nicht? Bei leerer Zeichenfolge wird der Dialog nie gezeigt."
+
+#: ../src/common/config.py:243
+msgid ""
+"If True, you will be able to set a negative priority to your account in "
+"account modification window. BE CAREFUL, when you are logged in with a "
+"negative priority, you will NOT receive any message from your server."
+msgstr ""
+"Wenn aktiviert, werden Sie in der Lage sein eine negative Priorität für Ihr "
+"Konto im Konto-Bearbeiten Fenster zu setzen. SEIEN SIE VORSICHTIG, wenn Sie "
+"mit einer negativen Priorität angemeldet sind: Sie werden KEINE Nachrichten "
+"mehr von Ihrem Server erhalten."
+
+#: ../src/common/config.py:244
+msgid ""
+"If True, Gajim will use Gnome Keyring (if available) to store account "
+"passwords."
+msgstr ""
+"Wenn aktiviert, wird Gnome Keyring (falls verfügbar) verwendet, um die "
+"Passwörter der Konten zu speichern."
+
+#: ../src/common/config.py:245
+msgid ""
+"If True, Gajim will show number of online and total contacts in account and "
+"group rows."
+msgstr ""
+"Wenn aktiviert, wird Gajim die Anzahl der angemeldeten und gesamten Kontakte "
+"in den Konto- und Gruppenreihen anzeigen."
+
+#: ../src/common/config.py:246
+msgid ""
+"Can be empty, 'chat' or 'normal'. If not empty, treat all incoming messages "
+"as if they were of this type"
+msgstr ""
+"Kann leer, 'chat' oder 'normal' sein. Wenn nicht leer, dann werden alle "
+"eingehenden Nachrichten so behandelt, als wären sie von diesem Typ."
+
+#: ../src/common/config.py:247
+msgid ""
+"If True, Gajim will scroll and select the contact who sent you the last "
+"message, if chat window is not already opened."
+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:248
+msgid ""
+"If True, Gajim will convert string between $$ and $$ to an image using dvips "
+"and convert before insterting it in chat window."
+msgstr ""
+"Falls 'true', wird Gajim eine Zeichenkette zwischen $$ und $$ mittels dvips "
+"vor dem Einfügen in ein Chat-Fenster in ein Bild umwandeln."
+
+#: ../src/common/config.py:249
+msgid "Time of inactivity needed before the change status window closes down."
+msgstr ""
+"Benötigte Inaktivitäts-Zeit bevor das 'Status ändern'-Fenster geschlossen "
+"wird."
+
+#: ../src/common/config.py:250
+msgid ""
+"Maximum number of lines that are printed in conversations. Oldest lines are "
+"cleared."
+msgstr ""
+"Maximale Anzahl Zeilen, die in einem Gespräch angezeigt werden. Die ältesten "
+"Zeilen werden gelöscht."
+
+#: ../src/common/config.py:251
+msgid ""
+"If True, notification windows from notification-daemon will be attached to "
+"systray icon."
+msgstr ""
+"Wenn wahr, werden Benachrichtigungsfenster vom Benachrichtigungsdienst an "
+"das Systray-Icon angehängt."
+
+#: ../src/common/config.py:252
+msgid "Choose interval between 2 checks of idleness."
+msgstr ""
+
+#: ../src/common/config.py:253
+msgid ""
+"Change the value to change the size of latex formulas displayed. The higher "
+"is larger."
+msgstr ""
+
+#: ../src/common/config.py:264
+msgid ""
+"Priority will change automatically according to your status. Priorities are "
+"defined in autopriority_* options."
+msgstr ""
+"Die Priorität wird automatisch dem Status entsprechend geändert. Prioritäten "
+"sind in den autopriority_* Optionen definiert."
+
+#: ../src/common/config.py:272
+#, fuzzy
+msgid ""
+"Status used to autoconnect as. Can be online, chat, away, xa, dnd, invisible."
+msgstr "Eins von: offline, online, chat, away, xa, dnd, invisible "
+
+#: ../src/common/config.py:277
+msgid ""
+"If disabled, don't sign presences with GPG key, even if GPG is configured."
+msgstr ""
+"Wenn deaktiviert, wird die Anwesenheit nicht mit einem GPG-Schlüssel "
+"signiert, wenn GPG konfiguriert ist."
+
+#: ../src/common/config.py:279
+msgid ""
+"Ordered list (space separated) of connection type to try. Can contain tls, "
+"ssl or plain"
+msgstr ""
+"Geordnete Liste (mit Leerzeichen getrennt) zu versuchender Verbindungstypen. "
+"Kann tls, ssl oder plain enthalten"
+
+#: ../src/common/config.py:280
+msgid ""
+"Show a warning dialog before sending password on an insecure connection."
+msgstr ""
+"Zeige einen Warndialog bevor das Passwort über eine unsichere Verbindung "
+"geschickt wird."
+
+#: ../src/common/config.py:282
+msgid "Space separated list of ssl errors to ignore."
+msgstr "Leerzeichen getrennte Liste von SSL-Fehler, die zu ignorieren sind."
+
+#: ../src/common/config.py:294
+msgid ""
+"How many seconds to wait for the answer of keepalive packet before we try to "
+"reconnect."
+msgstr ""
+
+#. yes, no, ask
+#: ../src/common/config.py:298
+msgid "Jabberd2 workaround"
+msgstr "Jabberd2 Workaround"
+
+#: ../src/common/config.py:302
+msgid ""
+"If checked, Gajim will use your IP and proxies defined in "
+"file_transfer_proxies option for file transfer."
+msgstr ""
+"Wenn aktiviert, wird Gajim Ihre IP und die in der file_transfer_proxies "
+"Option definierten Proxies für den Datentransfer verwenden."
+
+#: ../src/common/config.py:371
+msgid "Is OpenPGP enabled for this contact?"
+msgstr "Ist OpenPGP für diesen Kontakt aktiviert?"
+
+#: ../src/common/config.py:372 ../src/common/config.py:376
+msgid "Language for which we want to check misspelled words"
+msgstr "Sprache für, die nach falsch geschriebenen Wörtern gesucht werden soll"
+
+#: ../src/common/config.py:382
+msgid "all or space separated status"
+msgstr "alle oder Leerzeichen-getrennter Status"
+
+#: ../src/common/config.py:383
+msgid "'yes', 'no', or 'both'"
+msgstr "'ja', 'nein' oder 'beide'"
+
+#: ../src/common/config.py:384 ../src/common/config.py:386
+#: ../src/common/config.py:387 ../src/common/config.py:390
+#: ../src/common/config.py:391
+msgid "'yes', 'no' or ''"
+msgstr "'ja, 'nein' oder ''"
+
+#: ../src/common/config.py:397
+msgid "Sleeping"
+msgstr "Schlafen"
+
+#: ../src/common/config.py:398
+msgid "Back soon"
+msgstr "Bin gleich wieder da"
+
+#: ../src/common/config.py:398
+msgid "Back in some minutes."
+msgstr "Bin in ein paar Minuten zurück."
+
+#: ../src/common/config.py:399
+msgid "Eating"
+msgstr "Essen"
+
+#: ../src/common/config.py:399
+msgid "I'm eating, so leave me a message."
+msgstr "Ich esse gerade."
+
+#: ../src/common/config.py:400
+msgid "Movie"
+msgstr "Film"
+
+#: ../src/common/config.py:400
+msgid "I'm watching a movie."
+msgstr "Ich sehe mir einen Film an."
+
+#: ../src/common/config.py:401
+msgid "Working"
+msgstr "Arbeiten"
+
+#: ../src/common/config.py:401
+msgid "I'm working."
+msgstr "Ich arbeite."
+
+#: ../src/common/config.py:402
+msgid "Phone"
+msgstr "Telefon"
+
+#: ../src/common/config.py:402
+msgid "I'm on the phone."
+msgstr "Ich telefoniere."
+
+#: ../src/common/config.py:403
+msgid "Out"
+msgstr "Draußen"
+
+#: ../src/common/config.py:403
+msgid "I'm out enjoying life."
+msgstr "Ich bin draußen und genieße das Leben."
+
+#: ../src/common/config.py:407
+msgid "I'm available."
+msgstr "Ich bin angemeldet."
+
+#: ../src/common/config.py:408
+msgid "I'm free for chat."
+msgstr "Ich bin frei zum Chatten."
+
+#: ../src/common/config.py:409 ../src/config.py:1363
+msgid "Be right back."
+msgstr "Bin gleich zurück."
+
+#: ../src/common/config.py:410
+msgid "I'm not available."
+msgstr "Ich bin nicht verfügbar."
+
+#: ../src/common/config.py:411
+msgid "Do not disturb."
+msgstr "Bitte nicht stören."
+
+#: ../src/common/config.py:412 ../src/common/config.py:413
+msgid "Bye!"
+msgstr "Auf Wiedersehen!"
+
+#: ../src/common/config.py:423
+msgid ""
+"Sound to play when a group chat message contains one of the words in "
+"muc_highlight_words, or when a group chat message contains your nickname."
+msgstr ""
+"Abzuspielender Ton, falls eine Gruppenchat-Nachricht eines der Wörter aus "
+"der muc_highlights_works-Liste oder Ihren Spitznamen enthält."
+
+#: ../src/common/config.py:424
+msgid "Sound to play when any MUC message arrives."
+msgstr ""
+"Ton der beim empfangen einer Gruppenchat-Nachricht abgespielt werden soll"
+
+#: ../src/common/config.py:433 ../src/common/optparser.py:226
+msgid "green"
+msgstr "grün"
+
+#: ../src/common/config.py:437 ../src/common/optparser.py:212
+msgid "grocery"
+msgstr "gemüse"
+
+#: ../src/common/config.py:441
+msgid "human"
+msgstr "menschlich"
+
+#: ../src/common/config.py:445
+msgid "marine"
+msgstr "Marine"
+
+#: ../src/common/connection_handlers.py:69
+#: ../src/common/zeroconf/connection_handlers_zeroconf.py:49
+msgid "Unable to load idle module"
+msgstr "Konnte Idle-Modus nicht laden"
+
+#: ../src/common/connection_handlers.py:227
+#: ../src/common/zeroconf/connection_handlers_zeroconf.py:93
+msgid "Wrong host"
+msgstr "Falscher Host"
+
+#: ../src/common/connection_handlers.py:228
+msgid "Invalid local address? :-O"
+msgstr "Ungültige Lokale Adresse? :-O"
+
+#: ../src/common/connection_handlers.py:629
+#, python-format
+msgid "Registration information for transport %s has not arrived in time"
+msgstr ""
+"Registrierungsinformation für Transport %s sind nicht rechtzeitig angekommen"
+
+#: ../src/common/connection_handlers.py:906
+#: ../src/common/connection_handlers.py:1819
+#: ../src/common/connection_handlers.py:1869
+#: ../src/common/connection_handlers.py:2051
+#: ../src/common/connection_handlers.py:2163 ../src/common/connection.py:1155
+#: ../src/gajim.py:591
+msgid "Disk Write Error"
+msgstr "Fehler beim Schreiben auf Festplatte"
+
+#: ../src/common/connection_handlers.py:1704
+#, python-format
+msgid ""
+"%s has ended his/her private conversation with you. You should do the same."
+msgstr ""
+
+#: ../src/common/connection_handlers.py:1938
+#, python-format
+msgid "Nickname not allowed: %s"
+msgstr "Spitzname nicht erlaubt: %s"
+
+#. we are banned
+#. group chat does not exist
+#: ../src/common/connection_handlers.py:2008
+#: ../src/common/connection_handlers.py:2011
+#: ../src/common/connection_handlers.py:2014
+#: ../src/common/connection_handlers.py:2017
+#: ../src/common/connection_handlers.py:2021
+#: ../src/common/connection_handlers.py:2030
+msgid "Unable to join group chat"
+msgstr "Fehler beim Betreten des Gruppenchats"
+
+#: ../src/common/connection_handlers.py:2009
+#, python-format
+msgid "You are banned from group chat %s."
+msgstr "Sie sind von Gruppenchat %s gebannt."
+
+#: ../src/common/connection_handlers.py:2012
+#, python-format
+msgid "Group chat %s does not exist."
+msgstr "Dieser Gruppenchat existiert nicht."
+
+#: ../src/common/connection_handlers.py:2015
+msgid "Group chat creation is restricted."
+msgstr "Gruppenchaterstellung ist beschränkt."
+
+#: ../src/common/connection_handlers.py:2018
+#, python-format
+msgid "Your registered nickname must be used in group chat %s."
+msgstr "Sie müssen Ihren registrierten Spitznamen verwenden"
+
+#: ../src/common/connection_handlers.py:2022
+#, python-format
+msgid "You are not in the members list in groupchat %s."
+msgstr "Sie sind nicht in der Mitgliedliste"
+
+#: ../src/common/connection_handlers.py:2031
+#, python-format
+msgid ""
+"Your desired nickname in group chat %s is in use or registered by another "
+"occupant.\n"
+"Please specify another nickname below:"
+msgstr ""
+"Der gewünschte Benutzername wird bereits verwendet oder ist von einem "
+"anderen Benutzer registriert.\n"
+"Bitte geben Sie einen anderen Benutzernamen an:"
+
+#. Room has been destroyed. see
+#. http://www.xmpp.org/extensions/xep-0045.html#destroyroom
+#: ../src/common/connection_handlers.py:2062
+msgid "Room has been destroyed"
+msgstr "Raum wurde zerstört"
+
+#: ../src/common/connection_handlers.py:2069
+#, python-format
+msgid "You can join this room instead: %s"
+msgstr "Sie können stattdessen diesem Raum beitreten: %s"
+
+#: ../src/common/connection_handlers.py:2096
+msgid "I would like to add you to my roster."
+msgstr "Ich würde dich gerne zu meiner Liste hinzufügen."
+
+#. BE CAREFUL: no con.updateRosterItem() in a callback
+#: ../src/common/connection_handlers.py:2117
+#, python-format
+msgid "we are now subscribed to %s"
+msgstr "wir haben jetzt %s abonniert"
+
+#: ../src/common/connection_handlers.py:2119
+#, python-format
+msgid "unsubscribe request from %s"
+msgstr "Anfrage zur Kündigung des Abonnements von %s"
+
+#: ../src/common/connection_handlers.py:2121
+#, python-format
+msgid "we are now unsubscribed from %s"
+msgstr "%s hat das Abonnement beendet"
+
+#: ../src/common/connection_handlers.py:2262
+#, 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 ""
+"Jid %s ist nicht RFC-konform. Sie wird nicht zu ihrem Roster hinzugefügt. "
+"Verwenden Sie ein Roster-Managment-Tool wie <http://jru.jabberstudio.org/>, "
+"um ihn zu entfernen"
+
+#. We didn't set a passphrase
+#: ../src/common/connection_handlers.py:2287
+#: ../src/common/zeroconf/connection_zeroconf.py:172
+msgid "OpenPGP passphrase was not given"
+msgstr "Keine OpenPGP-Passphrase gewählt"
+
+#. %s is the account name here
+#: ../src/common/connection_handlers.py:2289
+#: ../src/common/zeroconf/connection_zeroconf.py:174
+#: ../src/roster_window.py:1830
+#, python-format
+msgid "You will be connected to %s without OpenPGP."
+msgstr "Sie werden ohne OpenPGP mit %s verbunden."
+
+#: ../src/common/connection.py:60
+msgid "Unable to get issuer certificate"
+msgstr "Kann Zertifikat des Ausstellers nicht holen"
+
+#: ../src/common/connection.py:61
+msgid "Unable to get certificate CRL"
+msgstr "Kann Zertifikats CRL nicht holen"
+
+#: ../src/common/connection.py:62
+msgid "Unable to decrypt certificate's signature"
+msgstr "Kann Zertifikatsunterschrift nicht entschlüsseln"
+
+#: ../src/common/connection.py:63
+msgid "Unable to decrypt CRL's signature"
+msgstr "Kann CRLs Unterschrift nicht entschlüsseln"
+
+#: ../src/common/connection.py:64
+msgid "Unable to decode issuer public key"
+msgstr "Kann öffentlichen Schlüssel nicht entschlüsseln"
+
+#: ../src/common/connection.py:65
+msgid "Certificate signature failure"
+msgstr "Zertifikat-Unterschrift-Fehler"
+
+#: ../src/common/connection.py:66
+msgid "CRL signature failure"
+msgstr "CRL-Unterschrift-Fehler"
+
+#: ../src/common/connection.py:67
+msgid "Certificate is not yet valid"
+msgstr "Zertifikat ist noch nicht gültig"
-#: ../src/config.py:129 ../src/config.py:633
+#: ../src/common/connection.py:68
+msgid "Certificate has expired"
+msgstr "Zertifikat ist abgelaufen"
+
+#: ../src/common/connection.py:69
+msgid "CRL is not yet valid"
+msgstr "CRL ist nocht nicht gültig"
+
+#: ../src/common/connection.py:70
+msgid "CRL has expired"
+msgstr "CRL ist abgelaufen"
+
+#: ../src/common/connection.py:71
+msgid "Format error in certificate's notBefore field"
+msgstr "Formatfehler im notBefore Feld des Zertifikats"
+
+#: ../src/common/connection.py:72
+msgid "Format error in certificate's notAfter field"
+msgstr "Formatfehler im notAfter Feld des Zertifikats"
+
+#: ../src/common/connection.py:73
+msgid "Format error in CRL's lastUpdate field"
+msgstr "Formatfehler im lastUpdate Feld des CRLs"
+
+#: ../src/common/connection.py:74
+msgid "Format error in CRL's nextUpdate field"
+msgstr "Formatfehler in nextUpdate Feld des CRLs"
+
+#: ../src/common/connection.py:75
+msgid "Out of memory"
+msgstr "Zu wenig Speicher"
+
+#: ../src/common/connection.py:76
+msgid "Self signed certificate"
+msgstr "Selbstsigniertes Zertifikat"
+
+#: ../src/common/connection.py:77
+msgid "Self signed certificate in certificate chain"
+msgstr "Selbstsigniertes Zertifikat in der Zertifikatkette"
+
+#: ../src/common/connection.py:78
+msgid "Unable to get local issuer certificate"
+msgstr "Kann lokales Herausgeberzertifikat nicht holen"
+
+#: ../src/common/connection.py:79
+msgid "Unable to verify the first certificate"
+msgstr "Kann erstes Zertifikat nicht prüfen"
+
+#: ../src/common/connection.py:80
+msgid "Certificate chain too long"
+msgstr "Zertifikatkette zu lang"
+
+#: ../src/common/connection.py:81
+msgid "Certificate revoked"
+msgstr "Zertifikat widerrufen"
+
+#: ../src/common/connection.py:82
+msgid "Invalid CA certificate"
+msgstr "Ungültiges CA Zertifikat"
+
+#: ../src/common/connection.py:83
+msgid "Path length constraint exceeded"
+msgstr "Pfadlängenbeschränkung überschritten"
+
+#: ../src/common/connection.py:84
+msgid "Unsupported certificate purpose"
+msgstr "Nicht unterstütztes Zertifikatsverwendungszweck"
+
+#: ../src/common/connection.py:85
+msgid "Certificate not trusted"
+msgstr "Zertifikat nicht vertrauenswürdig"
+
+#: ../src/common/connection.py:86
+msgid "Certificate rejected"
+msgstr "Zertifikat abgelehnt"
+
+#: ../src/common/connection.py:87
+msgid "Subject issuer mismatch"
+msgstr ""
+
+#: ../src/common/connection.py:88
+msgid "Authority and subject key identifier mismatch"
+msgstr ""
+
+#: ../src/common/connection.py:89
+msgid "Authority and issuer serial number mismatch"
+msgstr ""
+
+#: ../src/common/connection.py:90
+msgid "Key usage does not include certificate signing"
+msgstr ""
+
+#: ../src/common/connection.py:91
+msgid "Application verification failure"
+msgstr ""
+
+#: ../src/common/connection.py:254
+#: ../src/common/zeroconf/connection_zeroconf.py:214
+#, python-format
+msgid "Connection with account \"%s\" has been lost"
+msgstr "Verbindung mit Konto \"%s\" abgebrochen"
+
+#: ../src/common/connection.py:255
+msgid "Reconnect manually."
+msgstr "Manuelle Neuverbindung."
+
+#: ../src/common/connection.py:266
+#, python-format
+msgid "Server %s answered wrongly to register request: %s"
+msgstr "Server %s beantwortete Registrierungsanfrage nicht korrekt: %s"
+
+#: ../src/common/connection.py:300
+#, python-format
+msgid "Server %s provided a different registration form"
+msgstr "Server %s bot ein anderes Registrierungsformular"
+
+#: ../src/common/connection.py:316
+#, python-format
+msgid "Unknown SSL error: %d"
+msgstr "Unbekannter SSL-Fehler: %d"
+
+#. wrong answer
+#: ../src/common/connection.py:331
+msgid "Invalid answer"
+msgstr "Ungültige Antwort"
+
+#: ../src/common/connection.py:332
+#, python-format
+msgid "Transport %s answered wrongly to register request: %s"
+msgstr ""
+"Transport %s beantwortete unsere Registrierungsanfrage nicht korrekt: %s"
+
+#: ../src/common/connection.py:513
+msgid "Connection to proxy failed"
+msgstr "Verbindung mit Proxy fehlgeschlagen"
+
+#: ../src/common/connection.py:590 ../src/common/connection.py:670
+#: ../src/common/connection.py:1279
+#: ../src/common/zeroconf/connection_zeroconf.py:248
+#, python-format
+msgid "Could not connect to \"%s\""
+msgstr "Konnte nicht mit %s verbinden"
+
+#: ../src/common/connection.py:591 ../src/gajim.py:1116
+msgid "Check your connection or try again later."
+msgstr ""
+"Überprüfen Sie die Verbindung oder versuchen Sie es später noch einmal."
+
+#: ../src/common/connection.py:627
+#, python-format
+msgid "The authenticity of the %s certificate could be invalid."
+msgstr "Die Authentizität des %s Zertifikats könnte ungültig sein."
+
+#: ../src/common/connection.py:630
+#, python-format
+msgid ""
+"\n"
+"SSL Error: <b>%s</b>"
+msgstr ""
+"\n"
+"SSL-Fehler: <b>%s</b>"
+
+#: ../src/common/connection.py:632
+#, python-format
+msgid ""
+"\n"
+"Unknown SSL error: %d"
+msgstr ""
+"\n"
+"Unbekannter SSL-Fehler: %d"
+
+#: ../src/common/connection.py:671
+msgid "Check your connection or try again later"
+msgstr "Überprüfen Sie die Verbindung oder versuchen Sie es später noch einmal"
+
+#: ../src/common/connection.py:696
+#, python-format
+msgid "Authentication failed with \"%s\""
+msgstr "Authentifizierung mit \"%s\" fehlgeschlagen"
+
+#: ../src/common/connection.py:698
+msgid "Please check your login and password for correctness."
+msgstr "Bitte überprüfen Sie ihren Benutzernamen und Passwort."
+
+#: ../src/common/connection.py:759
+msgid "Error while removing privacy list"
+msgstr "Fehler beim Entfernen der Privatliste"
+
+#: ../src/common/connection.py:760
+#, python-format
+msgid ""
+"Privacy list %s has not been removed. It is maybe active in one of your "
+"connected resources. Deactivate it and try again."
+msgstr ""
+"Privatliste %s wurde nicht entfernt. Möglicherweise ist die Privatliste noch "
+"in einer Ihrer Verbindungen aktiv. Deaktivieren Sie diese und versuchen Sie "
+"es erneut."
+
+#: ../src/common/connection.py:1058
+#: ../src/common/zeroconf/connection_zeroconf.py:381
+msgid "Neither the remote presence is signed, nor a key was assigned."
+msgstr ""
+"Weder die entfernte Präsenz ist signiert, noch wurde ein Schlüssel "
+"zugewiesen."
+
+#: ../src/common/connection.py:1060
+#: ../src/common/zeroconf/connection_zeroconf.py:383
+#, python-format
+msgid "The contact's key (%s) does not match the key assigned in Gajim."
+msgstr ""
+"Der Kontaktschlüssel (%s) stimmt nicht mit dem in Gajim zugewiesenen "
+"Schlüssel überein."
+
+#. we're not english
+#. one in locale and one en
+#: ../src/common/connection.py:1069
+msgid "[This message is *encrypted* (See :XEP:`27`]"
+msgstr "[Diese Nachricht ist *verschlüsselt* (Siehe: JEP:`27`)]"
+
+#: ../src/common/connection.py:1146
+#: ../src/common/zeroconf/connection_zeroconf.py:445
+#, python-format
+msgid ""
+"Subject: %s\n"
+"%s"
+msgstr ""
+"Thema: %s\n"
+"%s"
+
+#: ../src/common/connection.py:1312
+msgid "Not fetched because of invisible status"
+msgstr "Nicht abgeholt aufgrund eines Unsichtbar-Status"
+
+#: ../src/common/contacts.py:304 ../src/common/contacts.py:319
+#: ../src/common/helpers.py:66 ../src/disco.py:111 ../src/disco.py:112
+#: ../src/disco.py:1320 ../src/gajim.py:913 ../src/roster_window.py:738
+#: ../src/roster_window.py:1367 ../src/roster_window.py:1413
+#: ../src/roster_window.py:1415 ../src/roster_window.py:1559
+msgid "Transports"
+msgstr "Transports"
+
+#: ../src/common/contacts.py:308
+msgid "Not in roster"
+msgstr "Nicht in der Kontaktliste"
+
+#: ../src/common/contacts.py:321 ../src/common/helpers.py:66
+#: ../src/roster_window.py:358 ../src/roster_window.py:635
+#: ../src/roster_window.py:691 ../src/roster_window.py:956
+#: ../src/roster_window.py:1205 ../src/roster_window.py:5011
+msgid "Observers"
+msgstr "Beobachter"
+
+#. only say that to non Windows users
+#: ../src/common/dbus_support.py:41
+msgid "D-Bus python bindings are missing in this computer"
+msgstr "Auf diesem Computer fehlen die Python Bibliotheken für D-Bus"
+
+#: ../src/common/dbus_support.py:42
+msgid "D-Bus capabilities of Gajim cannot be used"
+msgstr "Gajims D-BUS-Möglichkeiten können nicht genutzt werden"
+
+#: ../src/common/exceptions.py:27
+msgid "pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting..."
+msgstr ""
+"pysqlite2 (auch python-pysqlite2 genannt) Abhängigkeit fehlt. Beende ..."
+
+#: ../src/common/exceptions.py:44
+msgid "Database cannot be read."
+msgstr "Datenbank kann nicht gelesen werden."
+
+#: ../src/common/exceptions.py:52
+msgid "Service not available: Gajim is not running, or remote_control is False"
+msgstr ""
+"Dienst nicht verfügbar: Gajim ist nicht aktiv oder remote_control ist "
+"deaktiviert"
+
+#: ../src/common/exceptions.py:60
+msgid "D-Bus is not present on this machine or python module is missing"
+msgstr "D-Bus auf diesem Computer nicht vorhanden oder das Python-Modul fehlt"
+
+#: ../src/common/exceptions.py:68
+msgid ""
+"Session bus is not available.\n"
+"Try reading http://trac.gajim.org/wiki/GajimDBus"
+msgstr ""
+"Sitzungsbus ist nicht verfügbar.\"Bitte lesen Sie http://trac.gajim.org/wiki/"
+"GajimDBus (Englisch)"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "one"
+msgstr "Eins"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "two"
+msgstr "Zwei"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "three"
+msgstr "Drei"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "four"
+msgstr "Vier"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "five"
+msgstr "Fünf"
+
+#: ../src/common/fuzzyclock.py:47
+msgid "six"
+msgstr "Sechs"
+
+#: ../src/common/fuzzyclock.py:48
+msgid "seven"
+msgstr "Sieben"
+
+#: ../src/common/fuzzyclock.py:48
+msgid "eight"
+msgstr "Acht"
+
+#: ../src/common/fuzzyclock.py:48
+msgid "nine"
+msgstr "Neun"
+
+#: ../src/common/fuzzyclock.py:48
+msgid "ten"
+msgstr "Zehn"
+
+#: ../src/common/fuzzyclock.py:48
+msgid "eleven"
+msgstr "Elf"
+
+#: ../src/common/fuzzyclock.py:49
+msgid "twelve"
+msgstr "Zwölf"
+
+#. Strings to use for the output. %0 will be replaced with the preceding hour (e.g. "x PAST %0"), %1 with the coming hour (e.g. "x TO %1). '''
+#. A "singular-form". It is used when talking about hour 0
+#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60
+msgid "$0 o'clock"
+msgstr "%0 Uhr"
+
+#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60
+msgid "five past $0"
+msgstr "fünf nach %0"
+
+#: ../src/common/fuzzyclock.py:53 ../src/common/fuzzyclock.py:61
+msgid "ten past $0"
+msgstr "zehn nach %0"
+
+#: ../src/common/fuzzyclock.py:53 ../src/common/fuzzyclock.py:61
+msgid "quarter past $0"
+msgstr "viertel nach %0"
+
+#: ../src/common/fuzzyclock.py:54 ../src/common/fuzzyclock.py:62
+msgid "twenty past $0"
+msgstr "zwanzig nach %0"
+
+#: ../src/common/fuzzyclock.py:54 ../src/common/fuzzyclock.py:62
+msgid "twenty five past $0"
+msgstr "fünfundzwanzig nach %0"
+
+#: ../src/common/fuzzyclock.py:55 ../src/common/fuzzyclock.py:63
+msgid "half past $0"
+msgstr "dreißig nach %0"
+
+#: ../src/common/fuzzyclock.py:55 ../src/common/fuzzyclock.py:63
+msgid "twenty five to $1"
+msgstr "fünfundzwanzig vor %1"
+
+#: ../src/common/fuzzyclock.py:56 ../src/common/fuzzyclock.py:64
+msgid "twenty to $1"
+msgstr "zwanzig vor %1"
+
+#: ../src/common/fuzzyclock.py:56 ../src/common/fuzzyclock.py:64
+msgid "quarter to $1"
+msgstr "viertel vor %1"
+
+#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:65
+msgid "ten to $1"
+msgstr "zehn vor %1"
+
+#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:65
+msgid "five to $1"
+msgstr "fünf vor %1"
+
+#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:66
+msgid "$1 o'clock"
+msgstr "%1 Uhr"
+
+#: ../src/common/fuzzyclock.py:69
+msgid "Night"
+msgstr "Nacht"
+
+#: ../src/common/fuzzyclock.py:69
+msgid "Early morning"
+msgstr "Früh Morgens"
+
+#: ../src/common/fuzzyclock.py:69
+msgid "Morning"
+msgstr "Morgen"
+
+#: ../src/common/fuzzyclock.py:69
+msgid "Almost noon"
+msgstr "Fast Mittag"
+
+#: ../src/common/fuzzyclock.py:70
+msgid "Noon"
+msgstr "Mittag"
+
+#: ../src/common/fuzzyclock.py:70
+msgid "Afternoon"
+msgstr "Nachmittag"
+
+#: ../src/common/fuzzyclock.py:70
+msgid "Evening"
+msgstr "Abend"
+
+#: ../src/common/fuzzyclock.py:70
+msgid "Late evening"
+msgstr "Später Abend"
+
+#: ../src/common/fuzzyclock.py:72
+msgid "Start of week"
+msgstr "Anfang der Woche"
+
+#: ../src/common/fuzzyclock.py:72
+msgid "Middle of week"
+msgstr "Mitte der Woche"
+
+#: ../src/common/fuzzyclock.py:72
+msgid "End of week"
+msgstr "Ende der Woche"
+
+#: ../src/common/fuzzyclock.py:73
+msgid "Weekend!"
+msgstr "Wochenende!"
+
+#: ../src/common/helpers.py:137
+msgid "Invalid character in username."
+msgstr "Ungültiges Zeichen im Benutzernamen"
+
+#: ../src/common/helpers.py:142
+msgid "Server address required."
+msgstr "Server-Adresse wird benötigt."
+
+#: ../src/common/helpers.py:147
+msgid "Invalid character in hostname."
+msgstr "Ungültiges Zeichen in Hostname."
+
+#: ../src/common/helpers.py:153
+msgid "Invalid character in resource."
+msgstr "Ungültiges Zeichen in Resource."
+
+#. GiB means gibibyte
+#: ../src/common/helpers.py:193
+#, python-format
+msgid "%s GiB"
+msgstr "%s GiB"
+
+#. GB means gigabyte
+#: ../src/common/helpers.py:196
+#, python-format
+msgid "%s GB"
+msgstr "%s GB"
+
+#. MiB means mibibyte
+#: ../src/common/helpers.py:200
+#, python-format
+msgid "%s MiB"
+msgstr "%s MiB"
+
+#. MB means megabyte
+#: ../src/common/helpers.py:203
+#, python-format
+msgid "%s MB"
+msgstr "%s MB"
+
+#. KiB means kibibyte
+#: ../src/common/helpers.py:207
+#, python-format
+msgid "%s KiB"
+msgstr "%s KiB"
+
+#. KB means kilo bytes
+#: ../src/common/helpers.py:210
+#, python-format
+msgid "%s KB"
+msgstr "%s kB"
+
+#. B means bytes
+#: ../src/common/helpers.py:213
+#, python-format
+msgid "%s B"
+msgstr "%s B"
+
+#: ../src/common/helpers.py:244
+msgid "_Busy"
+msgstr "_Beschäftigt"
+
+#: ../src/common/helpers.py:246
+msgid "Busy"
+msgstr "Beschäftigt"
+
+#: ../src/common/helpers.py:249
+msgid "_Not Available"
+msgstr "_Nicht verfügbar"
+
+#: ../src/common/helpers.py:254
+msgid "_Free for Chat"
+msgstr "_Frei zum Chatten"
+
+#: ../src/common/helpers.py:256
+msgid "Free for Chat"
+msgstr "Frei zum Chatten"
+
+#: ../src/common/helpers.py:259
+msgid "_Available"
+msgstr "_Angemeldet"
+
+#: ../src/common/helpers.py:261 ../src/features_window.py:120
+msgid "Available"
+msgstr "Angemeldet"
+
+#: ../src/common/helpers.py:263
+msgid "Connecting"
+msgstr "Verbinde"
+
+#: ../src/common/helpers.py:266
+msgid "A_way"
+msgstr "Ab_wesend"
+
+#: ../src/common/helpers.py:271
+msgid "_Offline"
+msgstr "A_bgemeldet"
+
+#: ../src/common/helpers.py:273
+msgid "Offline"
+msgstr "Abgemeldet"
+
+#: ../src/common/helpers.py:276
+msgid "_Invisible"
+msgstr "_Unsichtbar"
+
+#: ../src/common/helpers.py:282
+msgid "?contact has status:Unknown"
+msgstr "Unbekannt"
+
+#: ../src/common/helpers.py:284
+msgid "?contact has status:Has errors"
+msgstr "Hat Fehler"
+
+#: ../src/common/helpers.py:289
+msgid "?Subscription we already have:None"
+msgstr "Keine"
+
+#: ../src/common/helpers.py:291
+msgid "To"
+msgstr "An"
+
+#: ../src/common/helpers.py:295
+msgid "Both"
+msgstr "Beide"
+
+#: ../src/common/helpers.py:303
+msgid "?Ask (for Subscription):None"
+msgstr "Keine"
+
+#: ../src/common/helpers.py:305
+msgid "Subscribe"
+msgstr "Abonnieren"
+
+#: ../src/common/helpers.py:314
+msgid "?Group Chat Contact Role:None"
+msgstr "Keine"
+
+#: ../src/common/helpers.py:317
+msgid "Moderators"
+msgstr "Moderatoren"
+
+#: ../src/common/helpers.py:319
+msgid "Moderator"
+msgstr "Moderator"
+
+#: ../src/common/helpers.py:322
+msgid "Participants"
+msgstr "Teilnehmer"
+
+#: ../src/common/helpers.py:324
+msgid "Participant"
+msgstr "Teilnehmer"
+
+#: ../src/common/helpers.py:327
+msgid "Visitors"
+msgstr "Besucher"
+
+#: ../src/common/helpers.py:329
+msgid "Visitor"
+msgstr "Besucher"
+
+#: ../src/common/helpers.py:335
+msgid "?Group Chat Contact Affiliation:None"
+msgstr "?Gruppenchat Kontaktverbindung:Keine"
+
+#: ../src/common/helpers.py:337
+msgid "Owner"
+msgstr "Besitzer"
+
+#: ../src/common/helpers.py:339
+msgid "Administrator"
+msgstr "Administrator"
+
+#: ../src/common/helpers.py:341
+msgid "Member"
+msgstr "Mitglied"
+
+#: ../src/common/helpers.py:348
+msgid "afraid"
+msgstr "Besorgt"
+
+#: ../src/common/helpers.py:348
+msgid "amazed"
+msgstr "Erstaunt"
+
+#: ../src/common/helpers.py:348
+msgid "angry"
+msgstr "Aufgebracht"
+
+#: ../src/common/helpers.py:349
+msgid "annoyed"
+msgstr "Verärgert"
+
+#: ../src/common/helpers.py:349
+msgid "anxious"
+msgstr "Bemüht"
+
+#: ../src/common/helpers.py:349
+msgid "aroused"
+msgstr "Erregt"
+
+#: ../src/common/helpers.py:350
+msgid "ashamed"
+msgstr "Beschämt"
+
+#: ../src/common/helpers.py:350
+msgid "bored"
+msgstr "Gelangweilt"
+
+#: ../src/common/helpers.py:350
+msgid "brave"
+msgstr "Tapfer"
+
+#: ../src/common/helpers.py:351
+msgid "calm"
+msgstr "Gelassen"
+
+#: ../src/common/helpers.py:351
+msgid "cold"
+msgstr "Kalt"
+
+#: ../src/common/helpers.py:351
+msgid "confused"
+msgstr "Verwirrt"
+
+#: ../src/common/helpers.py:352
+msgid "contented"
+msgstr "Zufrieden"
+
+#: ../src/common/helpers.py:352
+msgid "cranky"
+msgstr "Launisch"
+
+#: ../src/common/helpers.py:352
+msgid "curious"
+msgstr "Neugierig"
+
+#: ../src/common/helpers.py:353
+msgid "depressed"
+msgstr "Deprimiert"
+
+#: ../src/common/helpers.py:353
+msgid "disappointed"
+msgstr "Enttäuscht"
+
+#: ../src/common/helpers.py:354
+msgid "disgusted"
+msgstr "Empört"
+
+#: ../src/common/helpers.py:354
+msgid "distracted"
+msgstr "Abgelenkt"
+
+#: ../src/common/helpers.py:355
+msgid "embarrassed"
+msgstr "Verlegen"
+
+#: ../src/common/helpers.py:355
+msgid "excited"
+msgstr "Aufgeregt"
+
+#: ../src/common/helpers.py:356
+msgid "flirtatious"
+msgstr "Kokett"
+
+#: ../src/common/helpers.py:356
+msgid "frustrated"
+msgstr "Frustriert"
+
+#: ../src/common/helpers.py:357
+msgid "grumpy"
+msgstr "Mürrisch"
+
+#: ../src/common/helpers.py:357
+msgid "guilty"
+msgstr "Schuldig"
+
+#: ../src/common/helpers.py:357
+msgid "happy"
+msgstr "Fröhlich"
+
+#: ../src/common/helpers.py:358
+msgid "hot"
+msgstr "Heiß"
+
+#: ../src/common/helpers.py:358
+msgid "humbled"
+msgstr "Demütig"
+
+#: ../src/common/helpers.py:358
+msgid "humiliated"
+msgstr "Beschämt"
+
+#: ../src/common/helpers.py:359
+msgid "hungry"
+msgstr "Hungrig"
+
+#: ../src/common/helpers.py:359
+msgid "hurt"
+msgstr "Verletzt"
+
+#: ../src/common/helpers.py:359
+msgid "impressed"
+msgstr "Beeindruckt"
+
+#: ../src/common/helpers.py:360
+msgid "in awe"
+msgstr "Bewundernd"
+
+#: ../src/common/helpers.py:360
+msgid "in love"
+msgstr "Verliebt"
+
+#: ../src/common/helpers.py:360
+msgid "indignant"
+msgstr "Entrüstet"
+
+#: ../src/common/helpers.py:361
+msgid "interested"
+msgstr "Interessiert"
+
+#: ../src/common/helpers.py:361
+msgid "intoxicated"
+msgstr "Betrunken"
+
+#: ../src/common/helpers.py:362
+msgid "invincible"
+msgstr "unsichtbar"
+
+#: ../src/common/helpers.py:362
+msgid "jealous"
+msgstr "Neidisch"
+
+#: ../src/common/helpers.py:362
+msgid "lonely"
+msgstr "Einsam"
+
+#: ../src/common/helpers.py:363
+msgid "mean"
+msgstr "Fies"
+
+#: ../src/common/helpers.py:363
+msgid "moody"
+msgstr "Launisch"
+
+#: ../src/common/helpers.py:363
+msgid "nervous"
+msgstr "Nervös"
+
+#: ../src/common/helpers.py:364
+msgid "neutral"
+msgstr "Neutral"
+
+#: ../src/common/helpers.py:364
+msgid "offended"
+msgstr "Beleidigt"
+
+#: ../src/common/helpers.py:364
+msgid "playful"
+msgstr "Verspielt"
+
+#: ../src/common/helpers.py:365
+msgid "proud"
+msgstr "Stolz"
+
+#: ../src/common/helpers.py:365
+msgid "relieved"
+msgstr "Erleichtert"
+
+#: ../src/common/helpers.py:365
+msgid "remorseful"
+msgstr "Reumütig"
+
+#: ../src/common/helpers.py:366
+msgid "restless"
+msgstr "Ruhelos"
+
+#: ../src/common/helpers.py:366
+msgid "sad"
+msgstr "Traurig"
+
+#: ../src/common/helpers.py:366
+msgid "sarcastic"
+msgstr "Sarkastisch"
+
+#: ../src/common/helpers.py:367
+msgid "serious"
+msgstr "Ernst"
+
+#: ../src/common/helpers.py:367
+msgid "shocked"
+msgstr "Schockiert"
+
+#: ../src/common/helpers.py:367
+msgid "shy"
+msgstr "Schüchtern"
+
+#: ../src/common/helpers.py:368
+msgid "sick"
+msgstr "Krank"
+
+#: ../src/common/helpers.py:368
+msgid "sleepy"
+msgstr "Schläfrig"
+
+#: ../src/common/helpers.py:368
+msgid "stressed"
+msgstr "Gestreßt"
+
+#: ../src/common/helpers.py:369
+msgid "surprised"
+msgstr "Ãœberrascht"
+
+#: ../src/common/helpers.py:369
+msgid "thirsty"
+msgstr "Durstig"
+
+#: ../src/common/helpers.py:369
+msgid "worried"
+msgstr "Besorgt"
+
+#. Activities
+#: ../src/common/helpers.py:380
+msgid "working"
+msgstr "Arbeiten"
+
+#: ../src/common/helpers.py:380
+msgid "eating"
+msgstr "Essen"
+
+#: ../src/common/helpers.py:381
+msgid "excercising"
+msgstr ""
+
+#: ../src/common/helpers.py:381
+msgid "relaxing"
+msgstr "Entspannen"
+
+#: ../src/common/helpers.py:381
+msgid "talking"
+msgstr "Im Gespräch"
+
+#: ../src/common/helpers.py:382
+msgid "doing chores"
+msgstr "Mache den Haushalt"
+
+#: ../src/common/helpers.py:382
+msgid "inactive"
+msgstr "Inaktiv"
+
+#: ../src/common/helpers.py:383
+msgid "traveling"
+msgstr "Auf Reisen"
+
+#: ../src/common/helpers.py:383
+msgid "having an appointment"
+msgstr "Habe einen Termin"
+
+#: ../src/common/helpers.py:384
+msgid "grooming"
+msgstr "Putzen"
+
+#: ../src/common/helpers.py:384
+msgid "drinking"
+msgstr "In der Kneipe"
+
+#. Subactivites
+#: ../src/common/helpers.py:386
+msgid "on the phone"
+msgstr "Am Telefon"
+
+#: ../src/common/helpers.py:386
+msgid "gardening"
+msgstr "Gartenarbeit"
+
+#: ../src/common/helpers.py:387
+msgid "hiking"
+msgstr "Wandern"
+
+#: ../src/common/helpers.py:387
+msgid "on vacation"
+msgstr "Im Urlaub"
+
+#: ../src/common/helpers.py:387
+msgid "coding"
+msgstr "Programmieren"
+
+#: ../src/common/helpers.py:388
+msgid "walking"
+msgstr "Spazieren gehen"
+
+#: ../src/common/helpers.py:388
+msgid "rehearsing"
+msgstr "Studieren"
+
+#: ../src/common/helpers.py:388
+msgid "sleeping"
+msgstr "Schlafen"
+
+#: ../src/common/helpers.py:389
+msgid "brushing teeth"
+msgstr "Zähne putzen"
+
+#: ../src/common/helpers.py:389
+msgid "playing sports"
+msgstr "Beim Sport"
+
+#: ../src/common/helpers.py:390
+msgid "skiing"
+msgstr "Ski fahren"
+
+#: ../src/common/helpers.py:390
+msgid "having breakfast"
+msgstr "Frühstücken"
+
+#: ../src/common/helpers.py:391
+msgid "watching TV"
+msgstr "Fernsehen"
+
+#: ../src/common/helpers.py:391
+msgid "doing the dishes"
+msgstr "Geschirr abspülen"
+
+#: ../src/common/helpers.py:392
+msgid "at the spa"
+msgstr "Im Bad"
+
+#: ../src/common/helpers.py:392
+msgid "cycling"
+msgstr "Rad fahren"
+
+#: ../src/common/helpers.py:393
+msgid "hanging out"
+msgstr "Rumhängen"
+
+#: ../src/common/helpers.py:393
+msgid "driving"
+msgstr "Fahren"
+
+#: ../src/common/helpers.py:393
+msgid "commuting"
+msgstr "Pendeln"
+
+#: ../src/common/helpers.py:394
+msgid "cooking"
+msgstr "Kochen"
+
+#: ../src/common/helpers.py:394
+msgid "walking the dog"
+msgstr "Hund ausführen"
+
+#: ../src/common/helpers.py:395
+msgid "writing"
+msgstr "Schreiben"
+
+#: ../src/common/helpers.py:395
+msgid "on a trip"
+msgstr "Auf Reisen"
+
+#: ../src/common/helpers.py:395
+msgid "day off"
+msgstr "Freier Tag"
+
+#: ../src/common/helpers.py:396
+msgid "having tea"
+msgstr "Tee"
+
+#: ../src/common/helpers.py:396
+msgid "on a bus"
+msgstr "Im Bus"
+
+#: ../src/common/helpers.py:397
+msgid "having a beer"
+msgstr "Bier trinken"
+
+#: ../src/common/helpers.py:397
+msgid "reading"
+msgstr "Lesen"
+
+#: ../src/common/helpers.py:398
+msgid "buying groceries"
+msgstr "Lebensmittel einkaufen"
+
+#: ../src/common/helpers.py:398
+msgid "shaving"
+msgstr "Beim Rasieren"
+
+#: ../src/common/helpers.py:399
+msgid "getting a haircut"
+msgstr "Beim Frisör"
+
+#: ../src/common/helpers.py:399
+msgid "gaming"
+msgstr "Spielen"
+
+#: ../src/common/helpers.py:400
+msgid "having dinner"
+msgstr "Abendessen"
+
+#: ../src/common/helpers.py:400
+msgid "doing maintenance"
+msgstr "Wartungsarbeiten"
+
+#: ../src/common/helpers.py:401
+msgid "doing the laundry"
+msgstr "Wäsche waschen"
+
+#: ../src/common/helpers.py:401
+msgid "on video phone"
+msgstr "Bei einem Videotelefonat."
+
+#: ../src/common/helpers.py:402
+msgid "scheduled holiday"
+msgstr "Urlaub"
+
+#: ../src/common/helpers.py:402
+msgid "going out"
+msgstr "Ausgehen"
+
+#: ../src/common/helpers.py:403
+msgid "partying"
+msgstr "Feiern"
+
+#: ../src/common/helpers.py:403
+msgid "having a snack"
+msgstr "Beim Imbiss"
+
+#: ../src/common/helpers.py:404
+msgid "having lunch"
+msgstr "Mittagessen"
+
+#: ../src/common/helpers.py:404
+msgid "working out"
+msgstr "Ausarbeiten"
+
+#: ../src/common/helpers.py:405
+msgid "cleaning"
+msgstr "Putzen"
+
+#: ../src/common/helpers.py:405
+msgid "watching a movie"
+msgstr "Film schauen"
+
+#: ../src/common/helpers.py:406
+msgid "sunbathing"
+msgstr "Sonnenbaden"
+
+#: ../src/common/helpers.py:406
+msgid "socializing"
+msgstr "Kontakte knüpfen"
+
+#: ../src/common/helpers.py:407
+msgid "running an errand"
+msgstr "Besorgungen machen"
+
+#: ../src/common/helpers.py:407
+msgid "taking a bath"
+msgstr "Ein Bad nehmen"
+
+#: ../src/common/helpers.py:408
+msgid "in real life"
+msgstr "Im wahren Leben"
+
+#: ../src/common/helpers.py:408
+msgid "on a plane"
+msgstr "Im Flugzeug"
+
+#: ../src/common/helpers.py:409
+msgid "shopping"
+msgstr "Beim Einkaufen"
+
+#: ../src/common/helpers.py:409
+msgid "on a train"
+msgstr "Im Zug"
+
+#: ../src/common/helpers.py:409
+msgid "running"
+msgstr "Laufen"
+
+#: ../src/common/helpers.py:410
+msgid "taking a shower"
+msgstr "Unter der Dusche"
+
+#: ../src/common/helpers.py:410
+msgid "jogging"
+msgstr "Beim Joggen"
+
+#: ../src/common/helpers.py:411
+msgid "in a meeting"
+msgstr "In einer Besprechung"
+
+#: ../src/common/helpers.py:411
+msgid "in a car"
+msgstr "Im Auto"
+
+#: ../src/common/helpers.py:412
+msgid "studying"
+msgstr "Lerne"
+
+#: ../src/common/helpers.py:412
+msgid "swimming"
+msgstr "Schwimmen"
+
+#: ../src/common/helpers.py:413
+msgid "having coffee"
+msgstr "Kaffee trinken"
+
+#: ../src/common/helpers.py:452
+msgid "is paying attention to the conversation"
+msgstr "beobachtet diese Unterhaltung"
+
+#: ../src/common/helpers.py:454
+msgid "is doing something else"
+msgstr "tut etwas anderes"
+
+#: ../src/common/helpers.py:456
+msgid "is composing a message..."
+msgstr "schreibt im Moment ..."
+
+#. paused means he or she was composing but has stopped for a while
+#: ../src/common/helpers.py:459
+msgid "paused composing a message"
+msgstr "macht gerade eine Schreibpause"
+
+#: ../src/common/helpers.py:461
+msgid "has closed the chat window or tab"
+msgstr "hat das Chatfenster oder den Tab geschlossen"
+
+#: ../src/common/helpers.py:1032 ../src/common/helpers.py:1039
+#, python-format
+msgid "%d message pending"
+msgid_plural "%d messages pending"
+msgstr[0] "%d Nachricht schwebend"
+msgstr[1] "%d Nachrichten schwebend"
+
+#: ../src/common/helpers.py:1045
+#, python-format
+msgid " from room %s"
+msgstr "Von Gruppenchat %s"
+
+#: ../src/common/helpers.py:1048 ../src/common/helpers.py:1067
+#, python-format
+msgid " from user %s"
+msgstr "Von Benutzer %s"
+
+#: ../src/common/helpers.py:1050
+#, python-format
+msgid " from %s"
+msgstr "Von %s"
+
+#: ../src/common/helpers.py:1057 ../src/common/helpers.py:1064
+#, python-format
+msgid "%d event pending"
+msgid_plural "%d events pending"
+msgstr[0] "%d Ereignis anstehend"
+msgstr[1] "%d Ereignisse anstehend"
+
+#: ../src/common/helpers.py:1097
+#, python-format
+msgid "Gajim - %s"
+msgstr "Gajim - %s"
+
+#. we talk about a file
+#: ../src/common/optparser.py:65
+#, python-format
+msgid "error: cannot open %s for reading"
+msgstr "Fehler: %s kann nicht zum Lesen geöffnet werden"
+
+#: ../src/common/optparser.py:221 ../src/common/optparser.py:222
+msgid "cyan"
+msgstr "cyan"
+
+#: ../src/common/optparser.py:338
+msgid "migrating logs database to indices"
+msgstr "migriere Logdatenbank zu Indices"
+
+#: ../src/common/passwords.py:86
+#, python-format
+msgid "Gajim account %s"
+msgstr "Gajim-Konto %s"
+
+#: ../src/common/zeroconf/client_zeroconf.py:408
+msgid ""
+"Connection to host could not be established: Timeout while sending data."
+msgstr ""
+"Verbindung zum Host konnte nicht hergestellt werden: Zeitüberschreitung beim "
+"Senden von Daten."
+
+#: ../src/common/zeroconf/connection_handlers_zeroconf.py:93
+#, 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 %s, den Sie für die erweiterte Option ft_override_host_to_send "
+"angeben haben ist ungültig und wird ignoriert."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:215
+msgid "To continue sending and receiving messages, you will need to reconnect."
+msgstr ""
+"Um weiterhin Nachrichten Senden und Empfangen zu können, müssen Sie sich "
+"erneut verbinden."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:238
+msgid "Avahi error"
+msgstr "Avahi-Fehler"
+
+#: ../src/common/zeroconf/connection_zeroconf.py:238
+#, python-format
+msgid ""
+"%s\n"
+"Link-local messaging might not work properly."
+msgstr ""
+"%s\n"
+"Lokaler Nachrichtenversand funktioniert eventuell nicht richtig."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:249
+msgid "Please check if Avahi or Bonjour is installed."
+msgstr "Bitte überprüfen Sie, ob Avahi oder Bonjour installiert ist."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:258
+#: ../src/common/zeroconf/connection_zeroconf.py:262
+msgid "Could not start local service"
+msgstr "Lokaler Dienst konnte nicht gestartet werden"
+
+#: ../src/common/zeroconf/connection_zeroconf.py:259
+#, python-format
+msgid "Unable to bind to port %d."
+msgstr "Konnte nicht mit Port %d verbinden."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:263
+#: ../src/common/zeroconf/connection_zeroconf.py:358
+msgid "Please check if avahi-daemon is running."
+msgstr "Bitte überprüfen Sie, ob avahi-daemon läuft."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:357
+#, python-format
+msgid "Could not change status of account \"%s\""
+msgstr "Der Status des Kontos \"%s\" konnte nicht geändert werden."
+
+#: ../src/common/zeroconf/connection_zeroconf.py:374
+msgid ""
+"You are not connected or not visible to others. Your message could not be "
+"sent."
+msgstr ""
+"Sie sind nicht verbunden oder für andere nicht sichtbar. Ihre Nachricht "
+"konnte nicht versendet werden."
+
+#. we're not english
+#: ../src/common/zeroconf/connection_zeroconf.py:391
+msgid "[This message is encrypted]"
+msgstr "[Diese Nachricht ist verschlüsselt]"
+
+#: ../src/common/zeroconf/connection_zeroconf.py:456
+msgid "Your message could not be sent."
+msgstr "Die Nachricht konnte nicht gesendet werden."
+
+#. Contact Offline
+#: ../src/common/zeroconf/connection_zeroconf.py:463
+msgid "Contact is offline. Your message could not be sent."
+msgstr "Kontakt ist offline. Ihre Nachricht konnte nicht versendet werden."
+
+#: ../src/common/zeroconf/zeroconf_avahi.py:183
+#: ../src/common/zeroconf/zeroconf_bonjour.py:194
+#, python-format
+msgid "Error while adding service. %s"
+msgstr "Fehler beim Hinzufügen des Dienstes. %s"
+
+#: ../src/config.py:124 ../src/config.py:546
msgid "Disabled"
msgstr "Deaktiviert"
-#: ../src/config.py:332
+#: ../src/config.py:303
msgid "Active"
msgstr "Aktiv"
-#: ../src/config.py:340
+#: ../src/config.py:311
msgid "Event"
msgstr "Ereignis"
-#: ../src/config.py:465
+#: ../src/config.py:387
+#, fuzzy
+msgid "Default Message"
+msgstr "Vorgegebene Status-Nachrichten"
+
+#: ../src/config.py:394
+msgid "Enabled"
+msgstr "Aktiviert"
+
+#: ../src/config.py:436
msgid "Always use OS/X default applications"
msgstr ""
-#: ../src/config.py:466
+#: ../src/config.py:437
msgid "Custom"
msgstr "Benutzerdefiniert"
-#: ../src/config.py:702 ../src/dialogs.py:1148
+#: ../src/config.py:615 ../src/dialogs.py:1177
#, python-format
msgid "Dictionary for lang %s not available"
msgstr "Wörterburch für Sprache %s nicht verfügbar"
-#: ../src/config.py:703
+#: ../src/config.py:616
#, python-format
msgid ""
"You have to install %s dictionary to use spellchecking, or choose another "
@@ -3118,109 +5475,105 @@ msgstr ""
"Sie müssen das Wörterbuch %s installieren oder eine andere Sprache wählen, "
"um die Rechtschreibprüfung zu nutzen."
-#: ../src/config.py:1068
+#: ../src/config.py:981
msgid "status message title"
msgstr "Statusbetreff"
-#: ../src/config.py:1068
+#: ../src/config.py:981
msgid "status message text"
msgstr "Statusnachricht"
-#: ../src/config.py:1105
+#: ../src/config.py:1018
msgid "First Message Received"
msgstr "Erste empfangene Nachricht"
-#: ../src/config.py:1106
+#: ../src/config.py:1019
msgid "Next Message Received Focused"
msgstr "Nächste empfangene Nachricht fixiert"
-#: ../src/config.py:1108
+#: ../src/config.py:1021
msgid "Next Message Received Unfocused"
msgstr "Nächste empfangene Nachricht nicht fixiert"
-#: ../src/config.py:1109
+#: ../src/config.py:1022
msgid "Contact Connected"
msgstr "Kontakt verbunden"
-#: ../src/config.py:1110
+#: ../src/config.py:1023
msgid "Contact Disconnected"
msgstr "Kontakt nicht verbunden"
-#: ../src/config.py:1111
+#: ../src/config.py:1024
msgid "Message Sent"
msgstr "Nachricht gesendet"
-#: ../src/config.py:1112
+#: ../src/config.py:1025
msgid "Group Chat Message Highlight"
msgstr "Gruppenchat Nachrichten-Hervorhebung"
-#: ../src/config.py:1113
+#: ../src/config.py:1026
msgid "Group Chat Message Received"
msgstr "Gruppenchat-Nachricht empfangen"
-#: ../src/config.py:1114
+#: ../src/config.py:1027
msgid "GMail Email Received"
msgstr "E-Mail über Googlemail empfangen"
#. Name column
-#: ../src/config.py:1371 ../src/dialogs.py:1761 ../src/dialogs.py:1825
+#: ../src/config.py:1284 ../src/dialogs.py:1790 ../src/dialogs.py:1854
#: ../src/disco.py:742 ../src/disco.py:1534 ../src/disco.py:1780
-#: ../src/history_window.py:92
+#: ../src/history_window.py:90
msgid "Name"
msgstr "Name"
-#: ../src/config.py:1450 ../src/common/config.py:408
-msgid "Be right back."
-msgstr "Bin gleich zurück."
-
-#: ../src/config.py:1454
+#: ../src/config.py:1367
msgid "Relogin now?"
msgstr "Jetzt neu einloggen?"
-#: ../src/config.py:1455
+#: ../src/config.py:1368
msgid "If you want all the changes to apply instantly, you must relogin."
msgstr ""
"Wenn die Änderungen sofort übernommen werden sollen, müssen Sie sich neu "
"einloggen."
-#: ../src/config.py:1585 ../src/config.py:1684
+#: ../src/config.py:1497 ../src/config.py:1596
msgid "OpenPGP is not usable in this computer"
msgstr "OpenPGP kann auf diesem Computer nicht genutzt werden"
-#: ../src/config.py:1720 ../src/config.py:1761
+#: ../src/config.py:1632 ../src/config.py:1673
msgid "Unread events"
msgstr "Ungelesene Ereignisse"
-#: ../src/config.py:1721
+#: ../src/config.py:1633
msgid "Read all pending events before removing this account."
msgstr "Alle ungelesenen Ereignisse lesen, bevor der Account entfernt wird."
-#: ../src/config.py:1747
+#: ../src/config.py:1659
#, python-format
msgid "You have opened chat in account %s"
msgstr "Sie haben mit Account %s einen Chat geöffnet"
-#: ../src/config.py:1748
+#: ../src/config.py:1660
msgid "All chat and groupchat windows will be closed. Do you want to continue?"
msgstr "Alle Chatfenster werden geschlossen. Fortfahren?"
-#: ../src/config.py:1757
+#: ../src/config.py:1669
msgid "You are currently connected to the server"
msgstr "Sie sind mit dem Server verbunden"
-#: ../src/config.py:1758
+#: ../src/config.py:1670
msgid "To change the account name, you must be disconnected."
msgstr "Verbindung muss beendet werden, um Kontonamen zu ändern."
-#: ../src/config.py:1762
+#: ../src/config.py:1674
msgid "To change the account name, you must read all pending events."
msgstr "Um Kontonamen zu ändern, müssen Sie alle neunen Ereignisse lesen."
-#: ../src/config.py:1768
+#: ../src/config.py:1680
msgid "Account Name Already Used"
msgstr "Kontoname wird bereits verwendet"
-#: ../src/config.py:1769
+#: ../src/config.py:1681
msgid ""
"This name is already used by another of your accounts. Please choose another "
"name."
@@ -3228,149 +5581,149 @@ msgstr ""
"Dieser Name wird bereits für einen anderen Ihrer Accounts verwendet. Bitte "
"wählenSie einen anderen Namen."
-#: ../src/config.py:1773 ../src/config.py:1777
+#: ../src/config.py:1685 ../src/config.py:1689
msgid "Invalid account name"
msgstr "Ungültiger Kontoname"
-#: ../src/config.py:1774
+#: ../src/config.py:1686
msgid "Account name cannot be empty."
msgstr "Kontoname darf nicht leer sein."
-#: ../src/config.py:1778
+#: ../src/config.py:1690
msgid "Account name cannot contain spaces."
msgstr "Kontoname darf keine Leerzeichen enthalten."
-#: ../src/config.py:1842
+#: ../src/config.py:1761
msgid "Rename Account"
msgstr "Konto umbenennen"
-#: ../src/config.py:1843
+#: ../src/config.py:1762
#, python-format
msgid "Enter a new name for account %s"
msgstr "Geben Sie einen neuen Namen für das Konto %s ein"
-#: ../src/config.py:1861 ../src/config.py:1869 ../src/config.py:1911
-#: ../src/config.py:3128 ../src/dataforms_widget.py:535
+#: ../src/config.py:1780 ../src/config.py:1788 ../src/config.py:1830
+#: ../src/config.py:3053 ../src/dataforms_widget.py:535
msgid "Invalid Jabber ID"
msgstr "Ungültige Jabber ID"
-#: ../src/config.py:1870
+#: ../src/config.py:1789
msgid "A Jabber ID must be in the form \"user@servername\"."
msgstr "Jabber ID muss in der Form \"user@servername\" sein."
-#: ../src/config.py:2058 ../src/config.py:3200
+#: ../src/config.py:1977 ../src/config.py:3125
msgid "Invalid entry"
msgstr "Ungültiger Eintrag"
-#: ../src/config.py:2059 ../src/config.py:3201
+#: ../src/config.py:1978 ../src/config.py:3126
msgid "Custom port must be a port number."
msgstr "Proxy Port muss eine Portnummer sein."
-#: ../src/config.py:2080 ../src/config.py:3679
+#: ../src/config.py:1999 ../src/config.py:3606
msgid "Failed to get secret keys"
msgstr "Holen der geheimen Schlüssel fehlgeschlagen"
-#: ../src/config.py:2081 ../src/config.py:3680
+#: ../src/config.py:2000 ../src/config.py:3607
msgid "There was a problem retrieving your OpenPGP secret keys."
msgstr "Es gab ein Problem beim Holen ihres geheimen OpenPGP-Schlüssels."
-#: ../src/config.py:2084 ../src/config.py:3683
+#: ../src/config.py:2003 ../src/config.py:3610
msgid "OpenPGP Key Selection"
msgstr "OpenPGP Schlüssel-Auswahl"
-#: ../src/config.py:2085 ../src/config.py:3684
+#: ../src/config.py:2004 ../src/config.py:3611
msgid "Choose your OpenPGP key"
msgstr "Wählen Sie Ihren OpenPGP Schlüssel"
-#: ../src/config.py:2125
+#: ../src/config.py:2044
msgid "No such account available"
msgstr "Account nicht verfügbar"
-#: ../src/config.py:2126
+#: ../src/config.py:2045
msgid "You must create your account before editing your personal information."
msgstr ""
"Sie müssen ein Konto erstellen, bevor Sie die persönlichen Informationen "
"ändern können"
-#: ../src/config.py:2133 ../src/dialogs.py:1613 ../src/dialogs.py:1749
-#: ../src/dialogs.py:1929 ../src/disco.py:426 ../src/profile_window.py:318
+#: ../src/config.py:2052 ../src/dialogs.py:1642 ../src/dialogs.py:1778
+#: ../src/dialogs.py:1958 ../src/disco.py:426 ../src/profile_window.py:318
msgid "You are not connected to the server"
msgstr "Sie sind nicht mit dem Server verbunden"
-#: ../src/config.py:2134
+#: ../src/config.py:2053
msgid "Without a connection, you can not edit your personal information."
msgstr ""
"Sie müssen angemeldet sein, um Ihre persönlichen Informationen zu bearbeiten"
-#: ../src/config.py:2138
+#: ../src/config.py:2057
msgid "Your server doesn't support Vcard"
msgstr "Ihr Server unterstützt vCard nicht"
-#: ../src/config.py:2139
+#: ../src/config.py:2058
msgid "Your server can't save your personal information."
msgstr "Ihr Server kann keine persönlichen Informationen speichern."
-#: ../src/config.py:2170
+#: ../src/config.py:2089
msgid "Account Local already exists."
msgstr "Ein Konto mit dem Namen 'Local' ist bereits vorhanden"
-#: ../src/config.py:2171
+#: ../src/config.py:2090
msgid "Please rename or remove it before enabling link-local messaging."
msgstr ""
"Bitte benennen Sie es um oder entfernen es, bevor Sie LAN-Kontakte "
"aktivieren."
-#: ../src/config.py:2350
+#: ../src/config.py:2273
#, python-format
msgid "Edit %s"
msgstr "%s ändern"
-#: ../src/config.py:2352
+#: ../src/config.py:2275
#, python-format
msgid "Register to %s"
msgstr "Auf %s registrieren"
#. list at the beginning
-#: ../src/config.py:2388
+#: ../src/config.py:2311
msgid "Ban List"
msgstr "Sperrliste"
-#: ../src/config.py:2389
+#: ../src/config.py:2312
msgid "Member List"
msgstr "Mitgliederliste"
-#: ../src/config.py:2390
+#: ../src/config.py:2313
msgid "Owner List"
msgstr "Besitzerliste"
-#: ../src/config.py:2391
+#: ../src/config.py:2314
msgid "Administrator List"
msgstr "Administratorliste"
#. Address column
#. holds JID (who said this)
-#: ../src/config.py:2440 ../src/disco.py:749 ../src/history_manager.py:177
+#: ../src/config.py:2363 ../src/disco.py:749 ../src/history_manager.py:178
msgid "JID"
msgstr "JID"
-#: ../src/config.py:2448
+#: ../src/config.py:2371
msgid "Reason"
msgstr "Grund"
-#: ../src/config.py:2453
+#: ../src/config.py:2376
msgid "Nick"
msgstr "Spitzname"
-#: ../src/config.py:2457
+#: ../src/config.py:2380
msgid "Role"
msgstr "Rolle"
-#: ../src/config.py:2482
+#: ../src/config.py:2405
msgid "Banning..."
msgstr "Verbannen ..."
#. You can move '\n' before user@domain if that line is TOO BIG
-#: ../src/config.py:2484
+#: ../src/config.py:2407
msgid ""
"<b>Whom do you want to ban?</b>\n"
"\n"
@@ -3378,11 +5731,11 @@ msgstr ""
"<b>Wen möchten Sie verbannen?</b>\n"
"\n"
-#: ../src/config.py:2486
+#: ../src/config.py:2409
msgid "Adding Member..."
msgstr "Mitglied hinzufügen ..."
-#: ../src/config.py:2487
+#: ../src/config.py:2410
msgid ""
"<b>Whom do you want to make a member?</b>\n"
"\n"
@@ -3390,11 +5743,11 @@ msgstr ""
"<b>Wen möchten Sie zum Mitglied machen?</b>\n"
"\n"
-#: ../src/config.py:2489
+#: ../src/config.py:2412
msgid "Adding Owner..."
msgstr "Besitzer hinzufügen ..."
-#: ../src/config.py:2490
+#: ../src/config.py:2413
msgid ""
"<b>Whom do you want to make an owner?</b>\n"
"\n"
@@ -3402,11 +5755,11 @@ msgstr ""
"<b>Wen möchten Sie zum Besitzer machen?</b>\n"
"\n"
-#: ../src/config.py:2492
+#: ../src/config.py:2415
msgid "Adding Administrator..."
msgstr "Füge Administrator hinzu ..."
-#: ../src/config.py:2493
+#: ../src/config.py:2416
msgid ""
"<b>Whom do you want to make an administrator?</b>\n"
"\n"
@@ -3414,7 +5767,7 @@ msgstr ""
"<b>Wen möchten Sie zum Administrator machen?</b>\n"
"\n"
-#: ../src/config.py:2494
+#: ../src/config.py:2417
msgid ""
"Can be one of the following:\n"
"1. user@domain/resource (only that resource matches).\n"
@@ -3429,84 +5782,84 @@ msgstr ""
"3 domain (die Domain selbst trifft zu, als auch jeder benutzer@domain,\n"
"jede domain/resource oder Adresse, die eine Subdomain enthält."
-#: ../src/config.py:2600
+#: ../src/config.py:2523
#, python-format
msgid "Removing %s account"
msgstr "Entferne Konto %s"
-#: ../src/config.py:2615 ../src/gajim.py:1462 ../src/roster_window.py:1754
+#: ../src/config.py:2538 ../src/gajim.py:1484 ../src/roster_window.py:1811
msgid "Password Required"
msgstr "Passwort benötigt"
-#: ../src/config.py:2616 ../src/roster_window.py:1749
+#: ../src/config.py:2539 ../src/roster_window.py:1805
#, python-format
msgid "Enter your password for account %s"
msgstr "Geben Sie ihr Passwort für %s ein"
-#: ../src/config.py:2617 ../src/roster_window.py:1755
+#: ../src/config.py:2540 ../src/roster_window.py:1812
msgid "Save password"
msgstr "Passwort speichern"
-#: ../src/config.py:2630
+#: ../src/config.py:2553
#, python-format
msgid "Account \"%s\" is connected to the server"
msgstr "Konto \"%s\" ist mit Server verbunden"
-#: ../src/config.py:2631
+#: ../src/config.py:2554
msgid "If you remove it, the connection will be lost."
msgstr "Wenn Sie es entfernen, wird die Verbindung beendet."
-#: ../src/config.py:2725
+#: ../src/config.py:2650
msgid "Default"
msgstr "Standard"
-#: ../src/config.py:2725
+#: ../src/config.py:2650
msgid "?print_status:All"
msgstr "?print_status:Alle"
-#: ../src/config.py:2726
+#: ../src/config.py:2651
msgid "Enter and leave only"
msgstr "Nur betreten und verlassen"
-#: ../src/config.py:2727
+#: ../src/config.py:2652
msgid "?print_status:None"
msgstr "?print_status:Nichts"
-#: ../src/config.py:2796
+#: ../src/config.py:2721
msgid "New Group Chat"
msgstr "Neuer Gruppenchat"
-#: ../src/config.py:2829
+#: ../src/config.py:2754
msgid "This bookmark has invalid data"
msgstr "Dieses Lesezeichen hat ungültige Daten"
-#: ../src/config.py:2830
+#: ../src/config.py:2755
msgid ""
"Please be sure to fill out server and room fields or remove this bookmark."
msgstr "Bitte Serverfeld und Raumfeld ausfüllen oder Lesezeichen löschen."
-#: ../src/config.py:3111
+#: ../src/config.py:3036
msgid "Invalid username"
msgstr "Ungültiger Benutzername"
-#: ../src/config.py:3113
+#: ../src/config.py:3038
msgid "You must provide a username to configure this account."
msgstr ""
"Sie müssen einen Benutzernamen angeben, um diesen Account zu konfigurieren."
-#: ../src/config.py:3139
+#: ../src/config.py:3064
msgid "Duplicate Jabber ID"
msgstr "Doppelte Jabber ID"
-#: ../src/config.py:3140
+#: ../src/config.py:3065
msgid "This account is already configured in Gajim."
msgstr "Dieser Kontakt wurde in Gajim bereits konfiguriert."
-#: ../src/config.py:3157
+#: ../src/config.py:3082
msgid "Account has been added successfully"
msgstr "Account wurde erfolgreich hinzugefügt"
-#: ../src/config.py:3158 ../src/config.py:3345
+#: ../src/config.py:3083 ../src/config.py:3270
msgid ""
"You can set advanced account options by pressing the Advanced button, or "
"later by choosing the Accounts menuitem under the Edit menu from the main "
@@ -3516,24 +5869,24 @@ msgstr ""
"oder durch Klicken des Konto-Menüpunktes im Bearbeiten-Menü des "
"Hauptfensters erreichen."
-#: ../src/config.py:3176
+#: ../src/config.py:3101
msgid "Invalid server"
msgstr "Ungültiger Server"
-#: ../src/config.py:3177
+#: ../src/config.py:3102
msgid "Please provide a server on which you want to register."
msgstr "Bitte geben Sie an, bei dem Sie sich registrieren möchten."
-#: ../src/config.py:3228 ../src/gajim.py:2202
+#: ../src/config.py:3153 ../src/gajim.py:2190
msgid "Certificate Already in File"
msgstr "Zertifikat schon in der Datei"
-#: ../src/config.py:3229 ../src/gajim.py:2203
+#: ../src/config.py:3154 ../src/gajim.py:2191
#, python-format
msgid "This certificate is already in file %s, so it's not added again."
msgstr ""
-#: ../src/config.py:3297
+#: ../src/config.py:3222
#, python-format
msgid ""
"<b>Security Warning</b>\n"
@@ -3543,27 +5896,30 @@ msgid ""
"Do you still want to connect to this server?"
msgstr ""
-#: ../src/config.py:3303 ../src/gajim.py:2226
+#: ../src/config.py:3228 ../src/gajim.py:2214
#, python-format
msgid ""
"Add this certificate to the list of trusted certificates.\n"
"SHA1 fingerprint of the certificate:\n"
"%s"
msgstr ""
+"Dieses Zertifikat der Liste vertrauenswürdiger Zertifikate hinzufügen.\n"
+"SHA1-Fingerprint dieses Zertifikates:\n"
+"%s"
-#: ../src/config.py:3324 ../src/config.py:3363
+#: ../src/config.py:3249 ../src/config.py:3288
msgid "An error occurred during account creation"
msgstr "Während der Konto-Erstellung ist ein Fehler aufgetreten"
-#: ../src/config.py:3344
+#: ../src/config.py:3269
msgid "Your new account has been created successfully"
msgstr "Ihr neues Konto wurde erfolgreich erstellt"
-#: ../src/config.py:3447
+#: ../src/config.py:3372
msgid "Account name is in use"
msgstr "Kontoname ist schon vergeben"
-#: ../src/config.py:3448
+#: ../src/config.py:3373
msgid "You already have an account using this name."
msgstr "Sie haben bereits ein Konto mit diesem Namen."
@@ -3607,19 +5963,19 @@ msgstr "Im _Internet suchen"
msgid "Open as _Link"
msgstr "Als _Link öffnen"
-#: ../src/conversation_textview.py:1111
+#: ../src/conversation_textview.py:1104
msgid "Yesterday"
msgstr "Gestern"
#. the number is >= 2
#. %i is day in year (1-365), %d (1-31) we want %i
-#: ../src/conversation_textview.py:1115
+#: ../src/conversation_textview.py:1108
#, python-format
msgid "%i days ago"
msgstr "Vor %i Tagen"
#. if we have subject, show it too!
-#: ../src/conversation_textview.py:1149
+#: ../src/conversation_textview.py:1142
#, python-format
msgid "Subject: %s\n"
msgstr "Thema: %s\n"
@@ -3668,122 +6024,121 @@ msgstr "Schlüssel-ID"
msgid "Contact name"
msgstr "Name des Kontakts"
-#: ../src/dialogs.py:441
-#, fuzzy
+#: ../src/dialogs.py:461
msgid "Set Mood"
-msgstr "Stimmung:"
+msgstr "Stimmung setzen:"
-#: ../src/dialogs.py:484
+#: ../src/dialogs.py:513
#, python-format
msgid "%s Status Message"
msgstr "%s Status-Nachricht"
-#: ../src/dialogs.py:486
+#: ../src/dialogs.py:515
msgid "Status Message"
msgstr "Status-Nachricht"
-#: ../src/dialogs.py:586
+#: ../src/dialogs.py:615
msgid "Save as Preset Status Message"
msgstr "Als derzeitige Statusnachricht speichern"
-#: ../src/dialogs.py:587
+#: ../src/dialogs.py:616
msgid "Please type a name for this status message"
msgstr "Bitte geben Sie einen Namen für diese Statusnachricht ein:"
-#: ../src/dialogs.py:598
+#: ../src/dialogs.py:627
msgid "Overwrite Status Message?"
msgstr "Status-Nachricht überschreiben"
-#: ../src/dialogs.py:599
+#: ../src/dialogs.py:628
msgid ""
"This name is already used. Do you want to overwrite this status message?"
msgstr ""
"Dieser Name wird bereits verwendet. Möchten Sie die Status-Nachricht "
"überschreiben?"
-#: ../src/dialogs.py:615
+#: ../src/dialogs.py:644
msgid "AIM Address:"
msgstr "AIM-Adresse"
-#: ../src/dialogs.py:616
+#: ../src/dialogs.py:645
msgid "GG Number:"
msgstr "GG Nummer"
-#: ../src/dialogs.py:617
+#: ../src/dialogs.py:646
msgid "ICQ Number:"
msgstr "ICQ-Nummer"
-#: ../src/dialogs.py:618
+#: ../src/dialogs.py:647
msgid "MSN Address:"
msgstr "MSN-Adresse"
-#: ../src/dialogs.py:619
+#: ../src/dialogs.py:648
msgid "Yahoo! Address:"
msgstr "Yahoo!-Adresse"
-#: ../src/dialogs.py:656
+#: ../src/dialogs.py:685
#, python-format
msgid "Please fill in the data of the contact you want to add in account %s"
msgstr ""
"Bitte füllen Sie die Daten für den Kontakt aus, den Sie dem Konto %s "
"hinzufügen wollen"
-#: ../src/dialogs.py:658
+#: ../src/dialogs.py:687
msgid "Please fill in the data of the contact you want to add"
msgstr "Bitte geben sie die Daten des neuen Kontakts ein"
-#: ../src/dialogs.py:815 ../src/dialogs.py:821
+#: ../src/dialogs.py:844 ../src/dialogs.py:850
msgid "Invalid User ID"
msgstr "Ungültige Benutzer ID"
-#: ../src/dialogs.py:822
+#: ../src/dialogs.py:851
msgid "The user ID must not contain a resource."
msgstr "Die Benutzer-ID darf keine resource enthalten."
-#: ../src/dialogs.py:836
+#: ../src/dialogs.py:865
msgid "Contact already in roster"
msgstr "Kontakt bereits im Roster"
-#: ../src/dialogs.py:837
+#: ../src/dialogs.py:866
msgid "This contact is already listed in your roster."
msgstr "Der Kontakt befindet sich bereit in Ihrem Roster."
-#: ../src/dialogs.py:873
+#: ../src/dialogs.py:902
msgid "User ID:"
msgstr "_Benutzer-ID:"
-#: ../src/dialogs.py:931
+#: ../src/dialogs.py:960
msgid "A GTK+ jabber client"
msgstr "Ein GTK+ Jabber-Client"
-#: ../src/dialogs.py:932
+#: ../src/dialogs.py:961
msgid "GTK+ Version:"
msgstr "GTK+-Version:"
-#: ../src/dialogs.py:933
+#: ../src/dialogs.py:962
msgid "PyGTK Version:"
msgstr "PyGTK-Version"
-#: ../src/dialogs.py:943
+#: ../src/dialogs.py:972
msgid "Current Developers:"
msgstr "Derzeitige Entwickler:"
-#: ../src/dialogs.py:945
+#: ../src/dialogs.py:974
msgid "Past Developers:"
msgstr "Frühere Entwickler:"
-#: ../src/dialogs.py:951
+#: ../src/dialogs.py:980
msgid "THANKS:"
msgstr "DANKE:"
#. remove one english sentence
#. and add it manually as translatable
-#: ../src/dialogs.py:957
+#: ../src/dialogs.py:986
msgid "Last but not least, we would like to thank all the package maintainers."
msgstr "Zuletzt möchten wird gerne allen Paket-Verwaltern danken."
#. here you write your name in the form Name FamilyName <someone@somewhere>
-#: ../src/dialogs.py:970
+#: ../src/dialogs.py:999
msgid "translator-credits"
msgstr ""
"Fridtjof Busse\n"
@@ -3792,18 +6147,18 @@ msgstr ""
"Sebastian Schäfer <sarek@uliweb.de>\n"
"Nico Gulden"
-#: ../src/dialogs.py:1141
+#: ../src/dialogs.py:1170
#, python-format
msgid "Unable to bind to port %s."
msgstr "Konnte nicht an Port %s binden."
-#: ../src/dialogs.py:1142
+#: ../src/dialogs.py:1171
msgid ""
"Maybe you have another running instance of Gajim. File Transfer will be "
"cancelled."
msgstr "Möglicherweise läuft Gajim bereits. Dateitransfer wird abgebrochen."
-#: ../src/dialogs.py:1149
+#: ../src/dialogs.py:1178
#, python-format
msgid ""
"You have to install %s dictionary to use spellchecking, or choose another "
@@ -3816,91 +6171,90 @@ msgstr ""
"\n"
"Das Hervorheben falsch geschriebener Worte wird nicht genutzt"
-#: ../src/dialogs.py:1542
+#: ../src/dialogs.py:1571
#, python-format
msgid "Subscription request for account %s from %s"
msgstr "Abonnement-Anforderung für Konto %s von %s"
-#: ../src/dialogs.py:1545
+#: ../src/dialogs.py:1574
#, python-format
msgid "Subscription request from %s"
msgstr "Abonnement-Anforderung von %s"
-#: ../src/dialogs.py:1606 ../src/gajim.py:2791 ../src/roster_window.py:1147
+#: ../src/dialogs.py:1635 ../src/gajim.py:2761 ../src/roster_window.py:1161
#, python-format
msgid "You are already in group chat %s"
msgstr "Sie sind bereits im Gruppenchat %s"
-#: ../src/dialogs.py:1614
+#: ../src/dialogs.py:1643
msgid "You can not join a group chat unless you are connected."
msgstr "Sie können einem Gruppenchat erst beitreten, wenn Sie verbunden sind."
-#: ../src/dialogs.py:1633
+#: ../src/dialogs.py:1662
#, python-format
msgid "Join Group Chat with account %s"
msgstr "Betrete Gruppenchat mit Account %s"
-#: ../src/dialogs.py:1704
-#, fuzzy
+#: ../src/dialogs.py:1733
msgid "Invalid Nickname"
msgstr "Ungültiger Benutzername"
-#: ../src/dialogs.py:1705 ../src/groupchat_control.py:1297
-#: ../src/groupchat_control.py:1569
+#: ../src/dialogs.py:1734 ../src/groupchat_control.py:1329
+#: ../src/groupchat_control.py:1601
msgid "The nickname has not allowed characters."
msgstr "Die Jabber-ID für den Gruppenchat enthält nicht erlaubte Zeichen."
-#: ../src/dialogs.py:1709 ../src/dialogs.py:1715
-#: ../src/groupchat_control.py:1736
+#: ../src/dialogs.py:1738 ../src/dialogs.py:1744
+#: ../src/groupchat_control.py:1768
msgid "Invalid group chat Jabber ID"
msgstr "Ungültige Jabber-ID für den Gruppenchat"
-#: ../src/dialogs.py:1710 ../src/dialogs.py:1716
-#: ../src/groupchat_control.py:1737
+#: ../src/dialogs.py:1739 ../src/dialogs.py:1745
+#: ../src/groupchat_control.py:1769
msgid "The group chat Jabber ID has not allowed characters."
msgstr "Die Jabber-ID für den Gruppenchat enthält nicht erlaubte Zeichen."
-#: ../src/dialogs.py:1722
+#: ../src/dialogs.py:1751
msgid "This is not a group chat"
msgstr "Das ist kein Gruppenchat"
-#: ../src/dialogs.py:1723
+#: ../src/dialogs.py:1752
#, python-format
msgid "%s is not the name of a group chat."
msgstr "%s ist kein Name eines Gruppenchats."
-#: ../src/dialogs.py:1750
+#: ../src/dialogs.py:1779
msgid "Without a connection, you can not synchronise your contacts."
msgstr "Sie müssen verbunden sein, um Ihre Kontakte zu Synchronisieren."
-#: ../src/dialogs.py:1764
+#: ../src/dialogs.py:1793
msgid "Server"
msgstr "Server"
-#: ../src/dialogs.py:1797
+#: ../src/dialogs.py:1826
msgid "This account is not connected to the server"
msgstr "Konto \"%s\" ist nicht mit dem Server verbunden"
-#: ../src/dialogs.py:1798
+#: ../src/dialogs.py:1827
msgid "You cannot synchronize with an account unless it is connected."
msgstr ""
"Sie können nicht mit einem Konto Synchronisieren, solange es nicht verbunden "
"ist."
-#: ../src/dialogs.py:1822
+#: ../src/dialogs.py:1851
msgid "Synchronise"
msgstr "Synchronisieren"
-#: ../src/dialogs.py:1880
+#: ../src/dialogs.py:1909
#, python-format
msgid "Start Chat with account %s"
msgstr "Starte Chat mit Account %s"
-#: ../src/dialogs.py:1882
+#: ../src/dialogs.py:1911
msgid "Start Chat"
msgstr "Chat starten"
-#: ../src/dialogs.py:1883
+#: ../src/dialogs.py:1912
msgid ""
"Fill in the nickname or the Jabber ID of the contact you would like\n"
"to send a chat message to:"
@@ -3909,251 +6263,261 @@ msgstr ""
"an den Sie eine Chat-Nachricht schicken wollen:"
#. if offline or connecting
-#: ../src/dialogs.py:1908 ../src/dialogs.py:2282 ../src/dialogs.py:2423
+#: ../src/dialogs.py:1937 ../src/dialogs.py:2313 ../src/dialogs.py:2454
msgid "Connection not available"
msgstr "Verbindung nicht verfügbar"
-#: ../src/dialogs.py:1909 ../src/dialogs.py:2283 ../src/dialogs.py:2424
+#: ../src/dialogs.py:1938 ../src/dialogs.py:2314 ../src/dialogs.py:2455
#, python-format
msgid "Please make sure you are connected with \"%s\"."
msgstr "Vergewissern Sie sich, dass Sie mit \"%s\" verbunden sind."
-#: ../src/dialogs.py:1918 ../src/dialogs.py:1921
+#: ../src/dialogs.py:1947 ../src/dialogs.py:1950
msgid "Invalid JID"
msgstr "Ungültige JID"
-#: ../src/dialogs.py:1921
+#: ../src/dialogs.py:1950
#, python-format
msgid "Unable to parse \"%s\"."
msgstr "Kann \"%s\" nicht parsen."
-#: ../src/dialogs.py:1930
+#: ../src/dialogs.py:1959
msgid "Without a connection, you can not change your password."
msgstr "Sie müssen verbunden sein, um Ihr Passwort zu ändern"
-#: ../src/dialogs.py:1948
+#: ../src/dialogs.py:1977
msgid "Invalid password"
msgstr "Ungültiges Passwort"
-#: ../src/dialogs.py:1949
+#: ../src/dialogs.py:1978
msgid "You must enter a password."
msgstr "Sie müssen ein Passwort eingeben."
-#: ../src/dialogs.py:1953
+#: ../src/dialogs.py:1982
msgid "Passwords do not match"
msgstr "Passwörter stimmen nicht überein"
-#: ../src/dialogs.py:1954
+#: ../src/dialogs.py:1983
msgid "The passwords typed in both fields must be identical."
msgstr "Die Passwörter in beiden Feldern müssen identisch sein."
#. img to display
#. default value
-#: ../src/dialogs.py:1996 ../src/notify.py:242 ../src/notify.py:456
+#: ../src/dialogs.py:2027 ../src/notify.py:242 ../src/notify.py:456
+#: ../src/osx/growler.py:12
msgid "Contact Signed In"
msgstr "Kontakt hat sich angemeldet"
-#: ../src/dialogs.py:1998 ../src/notify.py:250 ../src/notify.py:458
+#: ../src/dialogs.py:2029 ../src/notify.py:250 ../src/notify.py:458
+#: ../src/osx/growler.py:12
msgid "Contact Signed Out"
msgstr "Kontakt hat sich abgemeldet"
#. chat message
-#: ../src/dialogs.py:2000 ../src/notify.py:273 ../src/notify.py:460
+#: ../src/dialogs.py:2031 ../src/notify.py:273 ../src/notify.py:460
+#: ../src/osx/growler.py:12
msgid "New Message"
msgstr "Neue Nachricht"
#. single message
-#: ../src/dialogs.py:2000 ../src/notify.py:254 ../src/notify.py:460
+#: ../src/dialogs.py:2031 ../src/notify.py:254 ../src/notify.py:460
+#: ../src/osx/growler.py:13
msgid "New Single Message"
msgstr "Neue einzelne Nachricht"
#. private message
-#: ../src/dialogs.py:2001 ../src/notify.py:261 ../src/notify.py:461
+#: ../src/dialogs.py:2032 ../src/notify.py:261 ../src/notify.py:461
+#: ../src/osx/growler.py:13
msgid "New Private Message"
msgstr "Neue private Nachricht"
-#: ../src/dialogs.py:2001 ../src/gajim.py:1655 ../src/notify.py:469
+#: ../src/dialogs.py:2032 ../src/gajim.py:1661 ../src/notify.py:469
+#: ../src/osx/growler.py:13
msgid "New E-mail"
msgstr "Neue E-Mail"
-#: ../src/dialogs.py:2003 ../src/gajim.py:1720 ../src/notify.py:463
+#: ../src/dialogs.py:2034 ../src/gajim.py:1726 ../src/notify.py:463
+#: ../src/osx/growler.py:14
msgid "File Transfer Request"
msgstr "Dateitransfer Anfrage"
-#: ../src/dialogs.py:2005 ../src/gajim.py:1627 ../src/gajim.py:1687
-#: ../src/notify.py:465
+#: ../src/dialogs.py:2036 ../src/gajim.py:1633 ../src/gajim.py:1693
+#: ../src/notify.py:465 ../src/osx/growler.py:14
msgid "File Transfer Error"
msgstr "Dateitransfer-Fehler"
-#: ../src/dialogs.py:2007 ../src/gajim.py:1759 ../src/gajim.py:1781
-#: ../src/gajim.py:1798 ../src/notify.py:467
+#: ../src/dialogs.py:2038 ../src/gajim.py:1765 ../src/gajim.py:1787
+#: ../src/gajim.py:1804 ../src/notify.py:467 ../src/osx/growler.py:15
msgid "File Transfer Completed"
msgstr "Dateitransfer beendet"
-#: ../src/dialogs.py:2008 ../src/gajim.py:1762 ../src/notify.py:467
+#: ../src/dialogs.py:2039 ../src/gajim.py:1768 ../src/notify.py:467
+#: ../src/osx/growler.py:15
msgid "File Transfer Stopped"
msgstr "Dateitransfer gestoppt"
-#: ../src/dialogs.py:2010 ../src/gajim.py:1483 ../src/notify.py:471
+#: ../src/dialogs.py:2041 ../src/gajim.py:1505 ../src/notify.py:471
+#: ../src/osx/growler.py:16
msgid "Groupchat Invitation"
msgstr "Gruppenchat-Einladung"
-#: ../src/dialogs.py:2012 ../src/notify.py:234 ../src/notify.py:473
+#: ../src/dialogs.py:2043 ../src/notify.py:234 ../src/notify.py:473
+#: ../src/osx/growler.py:16
msgid "Contact Changed Status"
msgstr "Kontakt hat Status verändert"
-#: ../src/dialogs.py:2201
+#: ../src/dialogs.py:2232
#, python-format
msgid "Single Message using account %s"
msgstr "Einzelne Nachricht mit Account %s"
-#: ../src/dialogs.py:2203
+#: ../src/dialogs.py:2234
#, python-format
msgid "Single Message in account %s"
msgstr "Einzelne Nachricht in Account %s"
-#: ../src/dialogs.py:2205
+#: ../src/dialogs.py:2236
msgid "Single Message"
msgstr "Einzelne Nachricht"
#. prepare UI for Sending
-#: ../src/dialogs.py:2208
+#: ../src/dialogs.py:2239
#, python-format
msgid "Send %s"
msgstr "Sende %s"
#. prepare UI for Receiving
-#: ../src/dialogs.py:2231
+#: ../src/dialogs.py:2262
#, python-format
msgid "Received %s"
msgstr "%s empfangen"
#. prepare UI for Receiving
-#: ../src/dialogs.py:2254
+#: ../src/dialogs.py:2285
#, python-format
msgid "Form %s"
msgstr "Von %s"
#. we create a new blank window to send and we preset RE: and to jid
-#: ../src/dialogs.py:2324
+#: ../src/dialogs.py:2355
#, python-format
msgid "RE: %s"
msgstr "RE: %s"
-#: ../src/dialogs.py:2325
+#: ../src/dialogs.py:2356
#, python-format
msgid "%s wrote:\n"
msgstr "%s schrieb:\n"
-#: ../src/dialogs.py:2369
+#: ../src/dialogs.py:2400
#, python-format
msgid "XML Console for %s"
msgstr "XML Konsole für %s"
-#: ../src/dialogs.py:2371
+#: ../src/dialogs.py:2402
msgid "XML Console"
msgstr "XML Konsole"
-#: ../src/dialogs.py:2494
+#: ../src/dialogs.py:2525
#, python-format
msgid "Privacy List <b><i>%s</i></b>"
msgstr "Privatliste <b><i>%s</i></b>"
-#: ../src/dialogs.py:2498
+#: ../src/dialogs.py:2529
#, python-format
msgid "Privacy List for %s"
msgstr "Privatsphären-Liste für %s"
-#: ../src/dialogs.py:2554
+#: ../src/dialogs.py:2585
#, python-format
msgid "Order: %s, action: %s, type: %s, value: %s"
msgstr "Sortierung: %s, Aktion: %s, Typ: %s, Wert: %s"
-#: ../src/dialogs.py:2557
+#: ../src/dialogs.py:2588
#, python-format
msgid "Order: %s, action: %s"
msgstr "Sortierung: %s, Aktion: %s"
-#: ../src/dialogs.py:2599
+#: ../src/dialogs.py:2630
msgid "<b>Edit a rule</b>"
msgstr "<b>Eine Regel bearbeiten</b>"
-#: ../src/dialogs.py:2686
+#: ../src/dialogs.py:2717
msgid "<b>Add a rule</b>"
msgstr "<b>Eine Regel hinzufügen</b>"
-#: ../src/dialogs.py:2782
+#: ../src/dialogs.py:2813
#, python-format
msgid "Privacy Lists for %s"
msgstr "Privatliste für %s"
-#: ../src/dialogs.py:2784
+#: ../src/dialogs.py:2815
msgid "Privacy Lists"
msgstr "Privatlisten"
-#: ../src/dialogs.py:2854
+#: ../src/dialogs.py:2885
msgid "Invalid List Name"
msgstr "Ungültiger Listenname"
-#: ../src/dialogs.py:2855
+#: ../src/dialogs.py:2886
msgid "You must enter a name to create a privacy list."
msgstr ""
"Sie müssen einen Namen eingeben um eine Privatsphären-Liste zu erstellen."
-#: ../src/dialogs.py:2892
+#: ../src/dialogs.py:2923
msgid "$Contact has invited you to join a discussion"
msgstr "$Contact hat Sie in den Gruppenchat %(room_jid)s eingeladen"
-#: ../src/dialogs.py:2894
+#: ../src/dialogs.py:2925
#, python-format
msgid "$Contact has invited you to group chat %(room_jid)s"
msgstr "$Contact hat Sie in den Gruppenchat %(room_jid)s eingeladen"
-#: ../src/dialogs.py:2907
+#: ../src/dialogs.py:2938
#, python-format
msgid "Comment: %s"
msgstr "Kommentar: %s"
-#: ../src/dialogs.py:2973
+#: ../src/dialogs.py:3004
msgid "Choose Sound"
msgstr "Sound wählen"
-#: ../src/dialogs.py:2983 ../src/dialogs.py:3034
+#: ../src/dialogs.py:3014 ../src/dialogs.py:3065
msgid "All files"
msgstr "Alle Dateien"
-#: ../src/dialogs.py:2988
+#: ../src/dialogs.py:3019
msgid "Wav Sounds"
msgstr "Wav Dateien"
-#: ../src/dialogs.py:3021
+#: ../src/dialogs.py:3052
msgid "Choose Image"
msgstr "Bild auswählen"
-#: ../src/dialogs.py:3039
+#: ../src/dialogs.py:3070
msgid "Images"
msgstr "Bilder"
-#: ../src/dialogs.py:3104
+#: ../src/dialogs.py:3135
#, python-format
msgid "When %s becomes:"
msgstr "Wenn %s wird:"
-#: ../src/dialogs.py:3106
+#: ../src/dialogs.py:3137
#, python-format
msgid "Adding Special Notification for %s"
msgstr "Füge speziellen Hinweis für %s hinzu"
#. # means number
-#: ../src/dialogs.py:3177
+#: ../src/dialogs.py:3208
msgid "#"
msgstr "Nr."
-#: ../src/dialogs.py:3183
+#: ../src/dialogs.py:3214
msgid "Condition"
msgstr "Bedingung"
-#: ../src/dialogs.py:3301
+#: ../src/dialogs.py:3332
msgid "when I am "
msgstr "wenn Ich bin "
@@ -4161,18 +6525,6 @@ msgstr "wenn Ich bin "
msgid "Others"
msgstr "Andere"
-#. When we redraw the group in remove_contact the
-#. contact does still exist and so the group is still showing
-#. the old numbers.
-#: ../src/disco.py:111 ../src/disco.py:112 ../src/disco.py:1320
-#: ../src/gajim.py:893 ../src/roster_window.py:730 ../src/roster_window.py:745
-#: ../src/roster_window.py:1336 ../src/roster_window.py:1382
-#: ../src/roster_window.py:1384 ../src/roster_window.py:1524
-#: ../src/common/contacts.py:303 ../src/common/contacts.py:318
-#: ../src/common/helpers.py:66
-msgid "Transports"
-msgstr "Transports"
-
#. conference is a category for listing mostly groupchats in service discovery
#: ../src/disco.py:114
msgid "Conference"
@@ -4469,9 +6821,8 @@ msgstr ""
"im Advanced Configuration Editor auf 'true' setzen."
#: ../src/features_window.py:98
-#, fuzzy
msgid "End to End Encryption"
-msgstr "OpenPGP-Verschlüsselung"
+msgstr "Punkt-zu-Punkt-Verschlüsselung"
#: ../src/features_window.py:99
msgid "Encrypting chatmessages."
@@ -4482,19 +6833,16 @@ msgid "Requires python-crypto."
msgstr "Erfordert python-crypto"
#: ../src/features_window.py:102
-#, fuzzy
msgid "Off the Record Encryption"
-msgstr "OpenPGP-Verschlüsselung"
+msgstr "OTR-Verschlüsselung"
#: ../src/features_window.py:103
-#, fuzzy
msgid "Encrypting chatmessages in a way that even works through gateways."
-msgstr "Chat Nachrichten werden mit gpg Schlüssel verschlüsselt"
+msgstr "Verschlüssltes Kommunizieren, welches auch über Transporte möglich ist"
#: ../src/features_window.py:104 ../src/features_window.py:105
-#, fuzzy
-msgid "Requires pyotr and libotr."
-msgstr "Erfordert python-dbus."
+msgid "Requires pyotr and libotr (see http://trac.gajim.org/wiki/OTR)."
+msgstr "Erfordert pyotr und libotr (http://trac.gajim.org/wiki/OTR)."
#: ../src/features_window.py:106
msgid "RST Generator"
@@ -4524,10 +6872,6 @@ msgstr "Anklickbare URLs im Chat-Fenster."
msgid "Requires python-sexy."
msgstr "Erfordert python-sexy."
-#: ../src/features_window.py:120 ../src/common/helpers.py:261
-msgid "Available"
-msgstr "Angemeldet"
-
#: ../src/features_window.py:127
msgid "Feature"
msgstr "Fähigkeit"
@@ -4557,7 +6901,7 @@ msgstr "Größe: %s"
#. You is a reply of who sent a file
#. You is a reply of who received a file
#: ../src/filetransfers_window.py:174 ../src/filetransfers_window.py:184
-#: ../src/history_manager.py:485
+#: ../src/history_manager.py:486
msgid "You"
msgstr "Sie"
@@ -4567,7 +6911,7 @@ msgid "Sender: %s"
msgstr "Gespeichert in: %s"
#: ../src/filetransfers_window.py:176 ../src/filetransfers_window.py:595
-#: ../src/tooltips.py:649
+#: ../src/tooltips.py:646
msgid "Recipient: "
msgstr "Empfänger: "
@@ -4606,7 +6950,7 @@ msgstr "Gegenseite hat Dateitransfer gestoppt"
msgid "Choose File to Send..."
msgstr "Datei auswählen ..."
-#: ../src/filetransfers_window.py:259 ../src/tooltips.py:689
+#: ../src/filetransfers_window.py:259 ../src/tooltips.py:686
msgid "Description: "
msgstr "Beschreibung: "
@@ -4704,11 +7048,11 @@ msgstr "Datei: "
msgid "It is not possible to send empty files"
msgstr "Es nicht möglich, leere Dateien zu versenden"
-#: ../src/filetransfers_window.py:591 ../src/tooltips.py:639
+#: ../src/filetransfers_window.py:591 ../src/tooltips.py:636
msgid "Name: "
msgstr "Name: "
-#: ../src/filetransfers_window.py:593 ../src/tooltips.py:643
+#: ../src/filetransfers_window.py:593 ../src/tooltips.py:640
msgid "Sender: "
msgstr "Absender: "
@@ -4716,36 +7060,36 @@ msgstr "Absender: "
msgid "Pause"
msgstr "Pause"
-#: ../src/gajim.py:71
+#: ../src/gajim.py:72
#, python-format
msgid "%s is not a valid loglevel"
msgstr "%s ist kein gültiger Loglevel."
-#: ../src/gajim.py:144
+#: ../src/gajim.py:145
msgid "Gajim needs X server to run. Quiting..."
msgstr "Gajim benötigt einen laufenden X-Server. Breche ab ..."
-#: ../src/gajim.py:174
+#: ../src/gajim.py:175
msgid "Gajim needs PyGTK 2.8 or above"
msgstr "Gajim benötigt PyGTK 2.6 oder höher"
-#: ../src/gajim.py:175
+#: ../src/gajim.py:176
msgid "Gajim needs PyGTK 2.8 or above to run. Quiting..."
msgstr "Gajim benötigt PyGTK 2.6 oder höher. Breche ab ..."
-#: ../src/gajim.py:177
+#: ../src/gajim.py:178
msgid "Gajim needs GTK 2.8 or above"
msgstr "Gajim benötigt GTK 2.6 oder höher"
-#: ../src/gajim.py:178
+#: ../src/gajim.py:179
msgid "Gajim needs GTK 2.8 or above to run. Quiting..."
msgstr "Gajim benötigt GTK 2.6+. Breche ab ..."
-#: ../src/gajim.py:183
+#: ../src/gajim.py:184
msgid "GTK+ runtime is missing libglade support"
msgstr "GTK+ runtine fehlt libglade-Unterstützung"
-#: ../src/gajim.py:185
+#: ../src/gajim.py:186
#, python-format
msgid ""
"Please remove your current GTK+ runtime and install the latest stable "
@@ -4754,22 +7098,22 @@ msgstr ""
"Bitte entfernen Sie Ihre derzeitige GTK+ Laufzeitumgebung und installieren "
"Sie die aktuelle Version von %s"
-#: ../src/gajim.py:187
+#: ../src/gajim.py:188
msgid ""
"Please make sure that GTK+ and PyGTK have libglade support in your system."
msgstr ""
"Bitte stellen Sie sicher, dass GTK+ und PyGTK auf Ihrem System libglade-"
"Unterstützung besitzen."
-#: ../src/gajim.py:192
+#: ../src/gajim.py:193
msgid "Gajim needs PySQLite2 to run"
msgstr "Gajim benötigt PySQLite2 zum Starten"
-#: ../src/gajim.py:200
+#: ../src/gajim.py:201
msgid "Gajim needs pywin32 to run"
msgstr "Gajim benötigt pywin32 zum Laufen"
-#: ../src/gajim.py:201
+#: ../src/gajim.py:202
#, python-format
msgid ""
"Please make sure that Pywin32 is installed on your system. You can get it at "
@@ -4778,25 +7122,36 @@ msgstr ""
"Bitte stellen Sie sicher das Pywin32 auf Ihrem System installiert ist. Sie "
"können es unter folgender URL herunterladen: %s "
-#: ../src/gajim.py:326
-#, fuzzy
+#: ../src/gajim.py:328
msgid "Generating..."
-msgstr "Essen"
+msgstr "Generiere ..."
+
+#: ../src/gajim.py:332
+#, python-format
+msgid "Generating a private key for %s..."
+msgstr ""
+
+#: ../src/gajim.py:354
+#, python-format
+msgid ""
+"Generating a private key for %s...\n"
+"done."
+msgstr ""
-#: ../src/gajim.py:373
+#: ../src/gajim.py:385
msgid ""
"\n"
-"This user has requested an Off-the-Record private conversation. However, "
-"you do not have a plugin to support that.\n"
+"This user has requested an Off-the-Record private conversation. However, you "
+"do not have a plugin to support that.\n"
"See http://otr.cypherpunks.ca/ for more information."
msgstr ""
#. set the icon to all newly opened wind
-#: ../src/gajim.py:548
+#: ../src/gajim.py:567
msgid "Gajim is already running"
msgstr "Gajim läuft bereits"
-#: ../src/gajim.py:549
+#: ../src/gajim.py:568
msgid ""
"Another instance of Gajim seems to be running\n"
"Run anyway?"
@@ -4804,53 +7159,45 @@ msgstr ""
"Eine andere Instanz von Gajim schein bereits zu laufen\n"
"Trotzdem starten?"
-#: ../src/gajim.py:572 ../src/common/connection_handlers.py:904
-#: ../src/common/connection_handlers.py:1667
-#: ../src/common/connection_handlers.py:1715
-#: ../src/common/connection_handlers.py:1979
-#: ../src/common/connection_handlers.py:2091 ../src/common/connection.py:1155
-msgid "Disk Write Error"
-msgstr "Fehler beim Schreiben auf Festplatte"
-
-#: ../src/gajim.py:676
+#: ../src/gajim.py:697
msgid "Do you accept this request?"
msgstr "Akzeptieren Sie diese Anfrage?"
-#: ../src/gajim.py:678
+#: ../src/gajim.py:699
#, python-format
msgid "Do you accept this request on account %s?"
msgstr "Akzeptieren Sie diese Anfrage vom %s Konto?"
-#: ../src/gajim.py:681
+#: ../src/gajim.py:702
#, python-format
msgid "HTTP (%s) Authorization for %s (id: %s)"
msgstr "HTTP (%s) Autorisierung für %s (id: %s)"
-#: ../src/gajim.py:729 ../src/notify.py:475
+#: ../src/gajim.py:750 ../src/notify.py:475 ../src/osx/growler.py:17
msgid "Connection Failed"
msgstr "Verbindung fehlgeschlagen"
-#. ('MSGNOTSENT', account, (jid, ierror_msg, msg, time))
-#: ../src/gajim.py:987 ../src/gajim.py:999
+#. ('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
+#: ../src/gajim.py:1007 ../src/gajim.py:1019
#, python-format
msgid "error while sending %s ( %s )"
msgstr "Fehler beim Senden von %s ( %s )"
-#: ../src/gajim.py:1032
+#: ../src/gajim.py:1052
msgid "Authorization accepted"
msgstr "Autorisierung akzeptiert"
-#: ../src/gajim.py:1033
+#: ../src/gajim.py:1053
#, python-format
msgid "The contact \"%s\" has authorized you to see his or her status."
msgstr "Kontakt \"%s\" hat Sie autorisiert seinen oder ihren Staus zu sehen."
-#: ../src/gajim.py:1052
+#: ../src/gajim.py:1072
#, python-format
msgid "Contact \"%s\" removed subscription from you"
msgstr "Kontakt \"%s\" hat das Abonnement zurückgezogen"
-#: ../src/gajim.py:1053
+#: ../src/gajim.py:1073
msgid ""
"You will always see him or her as offline.\n"
"Do you want to remove him or her from your contact list?"
@@ -4858,74 +7205,69 @@ msgstr ""
"Du wirst sie oder ihn immer als offline sehen.\n"
"Möchtest du sie oder ihn von deiner Kontaktliste entfernen?"
-#: ../src/gajim.py:1095
+#: ../src/gajim.py:1115
#, python-format
msgid "Contact with \"%s\" cannot be established"
msgstr "Kontakt mit \"%s\" konnte nicht hergestellt werden"
-#: ../src/gajim.py:1096 ../src/common/connection.py:593
-msgid "Check your connection or try again later."
-msgstr ""
-"Überprüfen Sie die Verbindung oder versuchen Sie es später noch einmal."
-
-#: ../src/gajim.py:1275 ../src/groupchat_control.py:1036
+#: ../src/gajim.py:1295 ../src/groupchat_control.py:1068
#, python-format
msgid "%s is now known as %s"
msgstr "%s heißt jetzt %s"
-#: ../src/gajim.py:1290 ../src/groupchat_control.py:1185
-#: ../src/roster_window.py:1860
+#: ../src/gajim.py:1310 ../src/groupchat_control.py:1217
+#: ../src/roster_window.py:1918
#, python-format
msgid "%s is now %s"
msgstr "%s ist jetzt %s"
#. Can be a presence (see chg_contact_status in groupchat_control.py)
#. Can be a message (see handle_event_gc_config_change in gajim.py)
-#: ../src/gajim.py:1410 ../src/groupchat_control.py:996
+#: ../src/gajim.py:1432 ../src/groupchat_control.py:1028
msgid "Any occupant is allowed to see your full JID"
msgstr "Jeder Teilnehmer darf Ihre volle JID sehen"
-#: ../src/gajim.py:1413
+#: ../src/gajim.py:1435
msgid "Room now shows unavailable member"
msgstr "Raum zeigt jetzt abwesende Teilnehmer"
-#: ../src/gajim.py:1415
+#: ../src/gajim.py:1437
msgid "room now does not show unavailable members"
msgstr "Raum zeigt jetzt abwesende Teilnehmer nicht an"
-#: ../src/gajim.py:1418
+#: ../src/gajim.py:1440
msgid "A non-privacy-related room configuration change has occurred"
msgstr ""
"Eine nicht Privatsphären-bezogene Raum-Konfigurations-Änderung ist "
"aufgetreten"
#. Can be a presence (see chg_contact_status in groupchat_control.py)
-#: ../src/gajim.py:1421
+#: ../src/gajim.py:1443
msgid "Room logging is now enabled"
msgstr "Raum-Mitschnitt ist jetzt aktiviert"
-#: ../src/gajim.py:1423
+#: ../src/gajim.py:1445
msgid "Room logging is now disabled"
msgstr "Raum-Mitschnitt ist jetzt deaktiviert"
-#: ../src/gajim.py:1425
+#: ../src/gajim.py:1447
msgid "Room is now non-anonymous"
msgstr "Raum ist jetzt un-anonym"
-#: ../src/gajim.py:1428
+#: ../src/gajim.py:1450
msgid "Room is now semi-anonymous"
msgstr "Raum ist jetzt semi-anonym"
-#: ../src/gajim.py:1431
+#: ../src/gajim.py:1453
msgid "Room is now fully-anonymous"
msgstr "Raum ist jetzt voll anonym"
-#: ../src/gajim.py:1463
+#: ../src/gajim.py:1485
#, python-format
msgid "A Password is required to join the room %s. Please type it."
msgstr "Für das Betreten des Gruppenchats-Raumes %s wird ein Passwort benötig."
-#: ../src/gajim.py:1497
+#: ../src/gajim.py:1519
msgid ""
"You configured Gajim to use GPG agent, but there is no GPG agent running or "
"it returned a wrong passphrase.\n"
@@ -4933,39 +7275,39 @@ msgstr ""
"Du hast Gajim für die Verwendung des GPG-Agenten konfiguriert, aber es läuft "
"kein GPG-Agent oder er gab eine falsche Passphrase zurück.\n"
-#: ../src/gajim.py:1499
+#: ../src/gajim.py:1521
msgid "You are currently connected without your OpenPGP key."
msgstr "Sie wurden ohne ihren GPG-Schlüssel verbunden"
-#: ../src/gajim.py:1502
+#: ../src/gajim.py:1524
msgid "Your passphrase is incorrect"
msgstr "Ihre Passphrase ist falsch"
-#: ../src/gajim.py:1519
+#: ../src/gajim.py:1541 ../src/secrets.py:44
msgid "Passphrase Required"
msgstr "Passphrase benötigt"
-#: ../src/gajim.py:1520
+#: ../src/gajim.py:1542
#, python-format
msgid "Enter GPG key passphrase for account %s."
msgstr "Geben Sie die GPG-Passphrase für das Konto %s ein."
-#: ../src/gajim.py:1532
+#: ../src/gajim.py:1554
msgid "Wrong Passphrase"
msgstr "Falsche Passphrase"
-#: ../src/gajim.py:1533
+#: ../src/gajim.py:1555
msgid "Please retype your GPG passphrase or press Cancel."
msgstr ""
"Bitte geben Sie Ihre GPG-Passphrase erneut ein oder klicken Sie auf "
"Abbrechen."
-#: ../src/gajim.py:1638
+#: ../src/gajim.py:1644
#, python-format
msgid "New mail on %(gmail_mail_address)s"
msgstr "Neue E-Mail auf %(gmail_mail_address)s"
-#: ../src/gajim.py:1640
+#: ../src/gajim.py:1646
#, python-format
msgid "You have %d new mail conversation"
msgid_plural "You have %d new mail conversations"
@@ -4975,7 +7317,7 @@ msgstr[1] "Sie haben %d ungelesene E-Mail-Nachrichten"
#. FIXME: emulate Gtalk client popups. find out what they parse and how
#. they decide what to show
#. each message has a 'From', 'Subject' and 'Snippet' field
-#: ../src/gajim.py:1649
+#: ../src/gajim.py:1655
#, python-format
msgid ""
"\n"
@@ -4984,38 +7326,38 @@ msgstr ""
"\n"
"Von: %(from_address)s"
-#: ../src/gajim.py:1717
+#: ../src/gajim.py:1723
#, python-format
msgid "%s wants to send you a file."
msgstr "%s möchte ihnen eine Datei senden."
-#: ../src/gajim.py:1782
+#: ../src/gajim.py:1788
#, python-format
msgid "You successfully received %(filename)s from %(name)s."
msgstr "Sie haben %(filename)s erfolgreich von %(name)s erhalten."
#. ft stopped
-#: ../src/gajim.py:1786
+#: ../src/gajim.py:1792
#, python-format
msgid "File transfer of %(filename)s from %(name)s stopped."
msgstr "Dateitransfer %(filename)s von %(name)s wurde gestoppt."
-#: ../src/gajim.py:1799
+#: ../src/gajim.py:1805
#, python-format
msgid "You successfully sent %(filename)s to %(name)s."
msgstr "Sie haben%(filename)s erfolgreich an %(name)s gesendet."
#. ft stopped
-#: ../src/gajim.py:1803
+#: ../src/gajim.py:1809
#, python-format
msgid "File transfer of %(filename)s to %(name)s stopped."
msgstr "Dateitransfer von %(filename)s an %(name)s wurde gestoppt."
-#: ../src/gajim.py:1930 ../src/gajim.py:1974
+#: ../src/gajim.py:1936 ../src/gajim.py:1980
msgid "Confirm these session options"
msgstr "Bestätigen Sie diese Sitzungs-Optionen"
-#: ../src/gajim.py:1931
+#: ../src/gajim.py:1937
#, python-format
msgid ""
"The remote client wants to negotiate an session with these features:\n"
@@ -5030,7 +7372,7 @@ msgstr ""
"\n"
"\t\tSind diese Einstellungen akzeptabel?"
-#: ../src/gajim.py:1975
+#: ../src/gajim.py:1981
#, python-format
msgid ""
"The remote client selected these options:\n"
@@ -5045,32 +7387,32 @@ msgstr ""
"\n"
"Mit der Sitzung fortfahren?"
-#: ../src/gajim.py:2112
+#: ../src/gajim.py:2100
msgid "Username Conflict"
msgstr "Benutzernamenkonflikt"
-#: ../src/gajim.py:2113
+#: ../src/gajim.py:2101
msgid "Please type a new username for your local account"
msgstr "Bitte geben Sie einen neuen Benutzernamen für Ihr lokales Konto ein"
-#: ../src/gajim.py:2128 ../src/gajim.py:2130
+#: ../src/gajim.py:2116 ../src/gajim.py:2118
msgid "Ping?"
msgstr "Ping?"
-#: ../src/gajim.py:2136 ../src/gajim.py:2138
+#: ../src/gajim.py:2124 ../src/gajim.py:2126
#, python-format
msgid "Pong! (%s s.)"
msgstr "Pong! (%s s.)"
-#: ../src/gajim.py:2142 ../src/gajim.py:2144
+#: ../src/gajim.py:2130 ../src/gajim.py:2132
msgid "Error."
msgstr "Fehler:"
-#: ../src/gajim.py:2169
+#: ../src/gajim.py:2157
msgid "Resource Conflict"
msgstr "Resourcenkonflikt"
-#: ../src/gajim.py:2170
+#: ../src/gajim.py:2158
msgid ""
"You are already connected to this account with the same resource. Please "
"type a new one"
@@ -5078,11 +7420,11 @@ msgstr ""
"Sie sind mit diesem Konto bereits mit derselben Ressource verbunden.Bitte "
"geben Sie eine neue ein"
-#: ../src/gajim.py:2223
+#: ../src/gajim.py:2211
msgid "Error verifying SSL certificate"
msgstr "Fehler bei der Überprüfung des SSL-Zertifikats"
-#: ../src/gajim.py:2224
+#: ../src/gajim.py:2212
#, python-format
msgid ""
"There was an error verifying the SSL certificate of your jabber server: %"
@@ -5093,15 +7435,15 @@ msgstr ""
"Servers auf: %(error)s\n"
"Möchtest du dich dennoch zum Server verbinden?"
-#: ../src/gajim.py:2229
+#: ../src/gajim.py:2217
msgid "Ignore this error for this certificate."
msgstr "Ignoriere den Fehler für dieses Zertifikat."
-#: ../src/gajim.py:2244
+#: ../src/gajim.py:2232
msgid "SSL certificate error"
msgstr "SSL-Zertifikat-Fehler"
-#: ../src/gajim.py:2245
+#: ../src/gajim.py:2233
#, python-format
msgid ""
"It seems the SSL certificate has changed or your connection is being "
@@ -5118,11 +7460,11 @@ msgstr ""
"\n"
"Möchten Sie noch verbinden und den Fingerprint des Zertifikats aktualisieren?"
-#: ../src/gajim.py:2264
+#: ../src/gajim.py:2252
msgid "Insecure connection"
msgstr "Unsichere Verbindung"
-#: ../src/gajim.py:2265
+#: ../src/gajim.py:2253
msgid ""
"You are about to send your password on an insecure connection. Are you sure "
"you want to do that?"
@@ -5130,26 +7472,26 @@ msgstr ""
"Sie sind dabei Ihr Passwort über eine unsichere Verbindung zu senden. Sind "
"Sie sicher, dass Sie das wollen?"
-#: ../src/gajim.py:2267 ../src/groupchat_control.py:1663
-#: ../src/roster_window.py:3602
+#: ../src/gajim.py:2255 ../src/groupchat_control.py:1695
+#: ../src/roster_window.py:3665
msgid "Do _not ask me again"
msgstr "_Nicht noch einmal fragen"
-#: ../src/gajim.py:2280
+#: ../src/gajim.py:2268
msgid "PEP node was not removed"
msgstr "PEP Knoten wurde nicht entfernt"
-#: ../src/gajim.py:2281
+#: ../src/gajim.py:2269
#, python-format
msgid "PEP node %s was not removed: %s"
msgstr "PEP Knoten %s wurde nicht entfernt: %s"
#. theme doesn't exist, disable emoticons
-#: ../src/gajim.py:2751 ../src/gajim.py:2772
+#: ../src/gajim.py:2719 ../src/gajim.py:2740
msgid "Emoticons disabled"
msgstr "Emoticons deaktiviert"
-#: ../src/gajim.py:2752
+#: ../src/gajim.py:2720
msgid ""
"Your configured emoticons theme has not been found, so emoticons have been "
"disabled."
@@ -5157,7 +7499,7 @@ msgstr ""
"Das konfigurierte Emoticon-Thema wurde nicht gefunden. Emoticans sind "
"deaktiviert."
-#: ../src/gajim.py:2773
+#: ../src/gajim.py:2741
msgid ""
"Your configured emoticons theme cannot been loaded. You maybe need to update "
"the format of emoticons.py file. See http://trac.gajim.org/wiki/Emoticons "
@@ -5167,49 +7509,42 @@ msgstr ""
"vielleicht das Format der Datei emoticons.py aktualisieren. Gehe auf http://"
"trac.gajim.org/wiki/Emoticons für mehr Informationen."
-#: ../src/gajim.py:2799 ../src/roster_window.py:1155
-#: ../src/roster_window.py:3137
+#: ../src/gajim.py:2769 ../src/roster_window.py:1169
+#: ../src/roster_window.py:3215
msgid "You cannot join a group chat while you are invisible"
msgstr ""
"Sie können einem Gruppenchat nicht beitreten, wenn Sie unsichtbar sind."
#. it is good to notify the user
#. in case he or she cannot see the output of the console
-#: ../src/gajim.py:3074
+#: ../src/gajim.py:3031
msgid "Could not save your settings and preferences"
msgstr "Konnte Einstellungen nicht speichern"
-#: ../src/gajim.py:3165
+#: ../src/gajim.py:3123
msgid "Bookmark already set"
msgstr "Lesezeichen existiert schon"
-#: ../src/gajim.py:3166
+#: ../src/gajim.py:3124
#, python-format
msgid "Group Chat \"%s\" is already in your bookmarks."
msgstr "Der Gruppenchat \"%s\" ist schon in den Lesezeichen."
-#: ../src/gajim.py:3179
+#: ../src/gajim.py:3137
msgid "Bookmark has been added successfully"
msgstr "Lesezeichen wurde erfolgreich hinzugefügt"
-#: ../src/gajim.py:3180
+#: ../src/gajim.py:3138
msgid "You can manage your bookmarks via Actions menu in your roster."
msgstr ""
"Sie können ihre Lesezeichen über das Aktionen-Menü in der Kontaktliste "
"bearbeiten"
-#. sorted alphanum
-#: ../src/gajim.py:3275 ../src/common/config.py:102
-#: ../src/common/config.py:429 ../src/common/optparser.py:212
-#: ../src/common/optparser.py:430 ../src/common/optparser.py:464
-msgid "default"
-msgstr "Standard"
-
-#: ../src/gajim.py:3361
+#: ../src/gajim.py:3325
msgid "Network Manager support not available"
msgstr "Unterstützung für Network Manager nicht verfügbar"
-#: ../src/gajim.py:3474
+#: ../src/gajim.py:3438
msgid "Session Management support not available (missing gnome.ui module)"
msgstr "Sitzungsmanagment-Unterstützung nicht verfügbar (Modul gnome.ui fehlt)"
@@ -5513,7 +7848,6 @@ msgid "Check if Gajim is running"
msgstr "Bitte überprüfen Sie, ob Gajim läuft"
#: ../src/gajim-remote.py:272 ../src/gajim-remote.py:282
-#, fuzzy
msgid "Shows or hides the ipython window"
msgstr "Ein- oder Ausblenden des Hauptfensters"
@@ -5627,110 +7961,120 @@ msgstr ""
"Sie sind nicht mehr im Gruppenchat \"%s\" oder \"%s\" hat den Gruppenchat "
"verlassen."
-#: ../src/groupchat_control.py:385
+#: ../src/groupchat_control.py:388
msgid "Insert Nickname"
msgstr "Spitzname einfügen"
-#: ../src/groupchat_control.py:924
+#: ../src/groupchat_control.py:540
+#, fuzzy
+msgid "Conversation with "
+msgstr "Unterhaltungsverlauf"
+
+#: ../src/groupchat_control.py:542
+#, fuzzy
+msgid "Continued conversation"
+msgstr "Bestätigung abbrechen"
+
+#: ../src/groupchat_control.py:956
msgid "Really send file?"
msgstr "Datei wirklich senden?"
-#: ../src/groupchat_control.py:925
+#: ../src/groupchat_control.py:957
#, python-format
msgid "If you send a file to %s, he/she will know your real Jabber ID."
msgstr ""
"Wenn Sie eine Datei an %s senden wird er/sie Ihre echte Jabber ID kennen."
#. Can be a message (see handle_event_gc_config_change in gajim.py)
-#: ../src/groupchat_control.py:999
+#: ../src/groupchat_control.py:1031
msgid "Room logging is enabled"
msgstr "Raum-Mitschnitt ist aktiviert"
-#: ../src/groupchat_control.py:1001
+#: ../src/groupchat_control.py:1033
msgid "A new room has been created"
msgstr "Ein neuer Raum wurde erstellt"
-#: ../src/groupchat_control.py:1004
+#: ../src/groupchat_control.py:1036
msgid "The server has assigned or modified your roomnick"
msgstr "Der Server hat Ihren Raum-Nick zugewiesen oder verändert"
#. do not print 'kicked by None'
-#: ../src/groupchat_control.py:1010
+#: ../src/groupchat_control.py:1042
#, python-format
msgid "%(nick)s has been kicked: %(reason)s"
msgstr "%(nick)s wurde rausgeschmissen: %(reason)s"
-#: ../src/groupchat_control.py:1014
+#: ../src/groupchat_control.py:1046
#, python-format
msgid "%(nick)s has been kicked by %(who)s: %(reason)s"
msgstr "%(nick)s wurde von %(who)s rausgeschmissen: %(reason)s"
#. do not print 'banned by None'
-#: ../src/groupchat_control.py:1021
+#: ../src/groupchat_control.py:1053
#, python-format
msgid "%(nick)s has been banned: %(reason)s"
msgstr "%(nick)s wurde gebannt: %(reason)s"
-#: ../src/groupchat_control.py:1025
+#: ../src/groupchat_control.py:1057
#, python-format
msgid "%(nick)s has been banned by %(who)s: %(reason)s"
msgstr "%(nick)s wurde von %(who)s gebannt: %(reason)s"
-#: ../src/groupchat_control.py:1034
+#: ../src/groupchat_control.py:1066
#, python-format
msgid "You are now known as %s"
msgstr "Sie heißen nun %s"
-#: ../src/groupchat_control.py:1070 ../src/groupchat_control.py:1074
-#: ../src/groupchat_control.py:1079
+#: ../src/groupchat_control.py:1102 ../src/groupchat_control.py:1106
+#: ../src/groupchat_control.py:1111
#, python-format
msgid "%(nick)s has been removed from the room (%(reason)s)"
msgstr "%(nick)s wurde von %(who)s rausgeschmissen: %(reason)s"
-#: ../src/groupchat_control.py:1071
+#: ../src/groupchat_control.py:1103
msgid "affiliation changed"
msgstr "Zugehörigkeit geändert: "
-#: ../src/groupchat_control.py:1076
+#: ../src/groupchat_control.py:1108
msgid "room configuration changed to members-only"
msgstr "Gruppenchatkonfiguration wurde auf ausschließlich Mitgleider geändert"
-#: ../src/groupchat_control.py:1081
+#: ../src/groupchat_control.py:1113
msgid "system shutdown"
msgstr "System wird heruntergefahren"
-#: ../src/groupchat_control.py:1178
+#: ../src/groupchat_control.py:1210
#, python-format
msgid "%s has left"
msgstr "%s ist gegangen"
-#: ../src/groupchat_control.py:1183
+#: ../src/groupchat_control.py:1215
#, python-format
msgid "%s has joined the group chat"
msgstr "%s hat den Gruppenchat betreten"
#. Invalid Nickname
#. invalid char
-#: ../src/groupchat_control.py:1296 ../src/groupchat_control.py:1568
+#: ../src/groupchat_control.py:1328 ../src/groupchat_control.py:1600
msgid "Invalid nickname"
msgstr "Ungültiger Benutzername"
-#: ../src/groupchat_control.py:1320 ../src/groupchat_control.py:1338
-#: ../src/groupchat_control.py:1422 ../src/groupchat_control.py:1439
+#: ../src/groupchat_control.py:1352 ../src/groupchat_control.py:1370
+#: ../src/groupchat_control.py:1454 ../src/groupchat_control.py:1471
#, python-format
msgid "Nickname not found: %s"
msgstr "Spitzname nicht gefunden: %s"
-#: ../src/groupchat_control.py:1354
+#: ../src/groupchat_control.py:1386
msgid "This group chat has no subject"
msgstr "Dieser Gruppenchat hat kein Thema"
-#: ../src/groupchat_control.py:1365
+#: ../src/groupchat_control.py:1397
#, python-format
msgid "Invited %(contact_jid)s to %(room_jid)s."
msgstr "%(contact_jid)s in %(room_jid)s eingeladen"
-#: ../src/groupchat_control.py:1495
+#: ../src/groupchat_control.py:1527
#, python-format
msgid ""
"Usage: /%s <nickname|JID> [reason], bans the JID from the group chat. The "
@@ -5743,7 +8087,7 @@ msgstr ""
"ein \"@\" enthält. Wenn die JID derzeit im Gruppenchat ist, wird er/sie/es "
"ebenfalls gebannt. Unterstützt KEINE Leerzeichen im Spitznamen."
-#: ../src/groupchat_control.py:1502
+#: ../src/groupchat_control.py:1534
#, python-format
msgid ""
"Usage: /%s <nickname>, opens a private chat window with the specified "
@@ -5752,7 +8096,7 @@ msgstr ""
"Verwendung: /%s <Spitzname>, öffnet ein privates Nachrichtenfenster mit dem "
"Inhaber des angegebenen Spitznamens."
-#: ../src/groupchat_control.py:1508
+#: ../src/groupchat_control.py:1540
#, python-format
msgid ""
"Usage: /%s [reason], closes the current window or tab, displaying reason if "
@@ -5761,7 +8105,7 @@ msgstr ""
"Bedienung: /%s [Grund], schließt das aktuelle Fenster oder Tab, mit Anzeige "
"eines Grundes, falls angegeben."
-#: ../src/groupchat_control.py:1514
+#: ../src/groupchat_control.py:1546
#, python-format
msgid ""
"Usage: /%s <JID> [reason], invites JID to the current group chat, optionally "
@@ -5770,7 +8114,7 @@ msgstr ""
"Bedienung: /%s <JID> [Grund], lädt die JID in den aktuellen Gruppenchat ein, "
"optional mit der Angabe eines Grundes."
-#: ../src/groupchat_control.py:1518
+#: ../src/groupchat_control.py:1550
#, python-format
msgid ""
"Usage: /%s <room>@<server>[/nickname], offers to join room@server optionally "
@@ -5779,7 +8123,7 @@ msgstr ""
"Bedienung: /%s <Raum>@<Server> [/Spitzname], bietet den Beitritt zu "
"Raum@Server an, optional mit Spitznamen."
-#: ../src/groupchat_control.py:1522
+#: ../src/groupchat_control.py:1554
#, python-format
msgid ""
"Usage: /%s <nickname> [reason], removes the occupant specified by nickname "
@@ -5790,7 +8134,7 @@ msgstr ""
"Gruppenchats und zeigt optional einen Grund. Unterstützt KEINE Leerzeichen "
"im Spitznamen."
-#: ../src/groupchat_control.py:1531
+#: ../src/groupchat_control.py:1563
#, python-format
msgid ""
"Usage: /%s <nickname> [message], opens a private message window and sends "
@@ -5800,25 +8144,25 @@ msgstr ""
"Nachrichtenfenster und sendet die Nachricht zum Inhaber des angegebenen "
"Spitznamens."
-#: ../src/groupchat_control.py:1536
+#: ../src/groupchat_control.py:1568
#, python-format
msgid "Usage: /%s <nickname>, changes your nickname in current group chat."
msgstr ""
"Bedienung: /%s <Spitzname>, ändert den Spitznamen im aktuellen Gruppenchat."
-#: ../src/groupchat_control.py:1540
+#: ../src/groupchat_control.py:1572
#, python-format
msgid "Usage: /%s , display the names of group chat occupants."
msgstr "Benutzung: /%s , zeigt die Namen der Gruppenchatbesucher."
-#: ../src/groupchat_control.py:1544
+#: ../src/groupchat_control.py:1576
#, python-format
msgid "Usage: /%s [topic], displays or updates the current group chat topic."
msgstr ""
"Benutzung: /%s [topic], zeigt oder aktualisiert das derzeitige Gruppenchat-"
"Thema."
-#: ../src/groupchat_control.py:1547
+#: ../src/groupchat_control.py:1579
#, python-format
msgid ""
"Usage: /%s <message>, sends a message without looking for other commands."
@@ -5826,41 +8170,41 @@ msgstr ""
"Benutzung: /%s <Nachricht>, sendet eine Nachricht ohne andere Befehle zu "
"beachten."
-#: ../src/groupchat_control.py:1657
+#: ../src/groupchat_control.py:1689
#, python-format
msgid "Are you sure you want to leave group chat \"%s\"?"
msgstr "Möchten Sie den Gruppenchat \"%s\" wirklich verlassen?"
-#: ../src/groupchat_control.py:1659
+#: ../src/groupchat_control.py:1691
msgid ""
"If you close this window, you will be disconnected from this group chat."
msgstr ""
"Wenn Sie dieses Fenster schließen, wird die Verbindung zu diesem Gruppenchat "
"geschlossen."
-#: ../src/groupchat_control.py:1695
+#: ../src/groupchat_control.py:1727
msgid "Changing Subject"
msgstr "Thema ändern"
-#: ../src/groupchat_control.py:1696
+#: ../src/groupchat_control.py:1728
msgid "Please specify the new subject:"
msgstr "Bitte bestimmen Sie ein neues Thema"
-#: ../src/groupchat_control.py:1707
+#: ../src/groupchat_control.py:1739
msgid "Changing Nickname"
msgstr "Spitzname ändern"
-#: ../src/groupchat_control.py:1708
+#: ../src/groupchat_control.py:1740
msgid "Please specify the new nickname you want to use:"
msgstr "Bitte geben Sie an, welchen Spitznamen Sie verwenden möchten:"
#. Ask for a reason
-#: ../src/groupchat_control.py:1723
+#: ../src/groupchat_control.py:1755
#, python-format
msgid "Destroying %s"
msgstr "Zerstöre %s"
-#: ../src/groupchat_control.py:1724
+#: ../src/groupchat_control.py:1756
msgid ""
"You are going to definitively destroy this room.\n"
"You may specify a reason below:"
@@ -5868,22 +8212,22 @@ msgstr ""
"Sie werden den Raum endgültig zerstören.\n"
"Sie können hier einen Grund angeben:"
-#: ../src/groupchat_control.py:1726
+#: ../src/groupchat_control.py:1758
msgid "You may also enter an alternate venue:"
msgstr "Sie können auch einen alternativen Raum eintragen:"
#. ask for reason
-#: ../src/groupchat_control.py:1910
+#: ../src/groupchat_control.py:1942
#, python-format
msgid "Kicking %s"
msgstr "%s rausschmeißen"
-#: ../src/groupchat_control.py:1911 ../src/groupchat_control.py:2215
+#: ../src/groupchat_control.py:1943 ../src/groupchat_control.py:2247
msgid "You may specify a reason below:"
msgstr "Sie können eine Begründung angeben:"
#. ask for reason
-#: ../src/groupchat_control.py:2214
+#: ../src/groupchat_control.py:2246
#, python-format
msgid "Banning %s"
msgstr "%s verbannen"
@@ -5960,28 +8304,33 @@ msgstr ""
msgid "Save Image as..."
msgstr "Bild speichern unter ..."
-#: ../src/history_manager.py:90
+#: ../src/history_manager.py:91
msgid "Cannot find history logs database"
msgstr "Kann Verlaufs-Datenkbank nicht finden"
#. holds jid
-#: ../src/history_manager.py:130
+#: ../src/history_manager.py:131
msgid "Contacts"
msgstr "Kontakte"
#. holds time
-#: ../src/history_manager.py:143 ../src/history_manager.py:183
-#: ../src/history_window.py:100
+#: ../src/history_manager.py:144 ../src/history_manager.py:184
+#: ../src/history_window.py:98
msgid "Date"
msgstr "Datum"
+#. holds nickname
+#: ../src/history_manager.py:150 ../src/history_manager.py:202
+msgid "Nickname"
+msgstr "Spitzname"
+
#. holds message
-#: ../src/history_manager.py:157 ../src/history_manager.py:189
-#: ../src/history_window.py:108
+#: ../src/history_manager.py:158 ../src/history_manager.py:190
+#: ../src/history_window.py:106
msgid "Message"
msgstr "Nachricht"
-#: ../src/history_manager.py:209
+#: ../src/history_manager.py:210
msgid ""
"Do you want to clean up the database? (STRONGLY NOT RECOMMENDED IF GAJIM IS "
"RUNNING)"
@@ -5989,7 +8338,7 @@ msgstr ""
"Möchten Sie die Datenbank aufräumen? (NICHT EMPFOHLEN, WENN GAJIM GERADE "
"LÄUFT)"
-#: ../src/history_manager.py:211
+#: ../src/history_manager.py:212
msgid ""
"Normally allocated database size will not be freed, it will just become "
"reusable. If you really want to reduce database filesize, click YES, else "
@@ -6003,52 +8352,52 @@ msgstr ""
"\n"
"Falls Sie JA klicken, warten Sie bitte einen Augenblick ..."
-#: ../src/history_manager.py:423
+#: ../src/history_manager.py:424
msgid "Exporting History Logs..."
msgstr "Exportiere Verlauf ..."
-#: ../src/history_manager.py:498
+#: ../src/history_manager.py:499
#, python-format
msgid "%(who)s on %(time)s said: %(message)s\n"
msgstr "%(who)s sagte um %(time)s: %(message)s\n"
-#: ../src/history_manager.py:535
+#: ../src/history_manager.py:536
msgid "Do you really want to delete logs of the selected contact?"
msgid_plural "Do you really want to delete logs of the selected contacts?"
msgstr[0] "Möchten Sie wirklich alle Logs des ausgewählten Kontakts löschen?"
msgstr[1] "Möchten Sie wirklich alle Logs der ausgewählten Kontakte löschen?"
-#: ../src/history_manager.py:539 ../src/history_manager.py:574
+#: ../src/history_manager.py:540 ../src/history_manager.py:575
msgid "This is an irreversible operation."
msgstr "Dies ist ein unwiderruflicher Vorgang."
-#: ../src/history_manager.py:571
+#: ../src/history_manager.py:572
msgid "Do you really want to delete the selected message?"
msgid_plural "Do you really want to delete the selected messages?"
msgstr[0] "Möchten Sie die ausgewählte Nachricht wirklich löschen?"
msgstr[1] "Möchten Sie die ausgewählten Nachrichten wirklich löschen?"
-#: ../src/history_window.py:282
+#: ../src/history_window.py:288
#, python-format
msgid "Conversation History with %s"
msgstr "Unterhaltungs-Verlauf mit %s"
-#: ../src/history_window.py:410
+#: ../src/history_window.py:406
#, python-format
msgid "%(nick)s is now %(status)s: %(status_msg)s"
msgstr "%(nick)s ist jezt %(status)s: %(status_msg)s"
-#: ../src/history_window.py:414 ../src/notify.py:229
+#: ../src/history_window.py:410 ../src/notify.py:229
#, python-format
msgid "%(nick)s is now %(status)s"
msgstr "%(nick)s ist jetzt %(status)s"
-#: ../src/history_window.py:420
+#: ../src/history_window.py:416
#, python-format
msgid "Status is now: %(status)s: %(status_msg)s"
msgstr "Status ist jetzt: %(status)s: %(status_msg)s"
-#: ../src/history_window.py:423
+#: ../src/history_window.py:419
#, python-format
msgid "Status is now: %(status)s"
msgstr "Status ist jetzt: %(status)s"
@@ -6061,23 +8410,23 @@ msgstr "Konnte Bild nicht laden"
msgid "Image is too big"
msgstr "Das Bild ist zu groß"
-#: ../src/message_window.py:394
+#: ../src/message_window.py:422
msgid "Chats"
msgstr "Chats"
-#: ../src/message_window.py:396
+#: ../src/message_window.py:424
msgid "Group Chats"
msgstr "Gruppenchat"
-#: ../src/message_window.py:398
+#: ../src/message_window.py:426
msgid "Private Chats"
msgstr "Private Chats"
-#: ../src/message_window.py:404
+#: ../src/message_window.py:432
msgid "Messages"
msgstr "Nachrichten"
-#: ../src/message_window.py:408
+#: ../src/message_window.py:436
#, python-format
msgid "%s - %s"
msgstr "%s - %s"
@@ -6159,22 +8508,27 @@ msgstr "Neue Nachricht von %(nickname)s"
msgid "New Message from %(nickname)s"
msgstr "Neue Nachricht von %(nickname)s"
+#: ../src/osx/growler.py:10
+#, fuzzy
+msgid "Generic"
+msgstr "Allgemein"
+
#: ../src/profile_window.py:54
msgid "Retrieving profile..."
msgstr "Empfange Profil ..."
-#: ../src/profile_window.py:107 ../src/roster_window.py:2638
+#: ../src/profile_window.py:107 ../src/roster_window.py:2687
msgid "File is empty"
msgstr "Datei ist leer"
-#: ../src/profile_window.py:110 ../src/roster_window.py:2641
+#: ../src/profile_window.py:110 ../src/roster_window.py:2690
msgid "File does not exist"
msgstr "Datei existiert nicht"
#. keep identation
#. unknown format
#: ../src/profile_window.py:124 ../src/profile_window.py:140
-#: ../src/roster_window.py:2643 ../src/roster_window.py:2654
+#: ../src/roster_window.py:2692 ../src/roster_window.py:2703
msgid "Could not load image"
msgstr "Konnte Bild nicht laden"
@@ -6207,80 +8561,55 @@ msgstr ""
"Bei der Veröffentlichung Ihrer persönlichen Informationen ist ein Fehler "
"aufgetreten, versuchen Sie es später nochmals."
-#: ../src/roster_window.py:265 ../src/roster_window.py:884
+#: ../src/roster_window.py:265 ../src/roster_window.py:886
msgid "Merged accounts"
msgstr "Alle Konten"
-#: ../src/roster_window.py:357 ../src/roster_window.py:638
-#: ../src/roster_window.py:683 ../src/roster_window.py:971
-#: ../src/roster_window.py:1191 ../src/roster_window.py:4933
-#: ../src/common/contacts.py:320 ../src/common/helpers.py:66
-msgid "Observers"
-msgstr "Beobachter"
-
-#. When we redraw the group in remove_contact the
-#. contact does still exist and so the group is still showing
-#. the old numbers.
-#. Make special context menu if group is Groupchats
-#: ../src/roster_window.py:701 ../src/roster_window.py:719
-#: ../src/roster_window.py:1390 ../src/roster_window.py:1392
-#: ../src/roster_window.py:2689 ../src/roster_window.py:4646
-#: ../src/common/commands.py:199 ../src/common/contacts.py:107
-#: ../src/common/helpers.py:66
-msgid "Groupchats"
-msgstr "Gruppenchat"
-
-#: ../src/roster_window.py:1696
+#: ../src/roster_window.py:1752
msgid "Authorization has been sent"
msgstr "Autorisierung wurde erneut gesendet"
-#: ../src/roster_window.py:1697
+#: ../src/roster_window.py:1753
#, python-format
msgid "Now \"%s\" will know your status."
msgstr "\"%s\" kennt jetzt ihren Status."
-#: ../src/roster_window.py:1717
+#: ../src/roster_window.py:1773
msgid "Subscription request has been sent"
msgstr "Abonnement-Anforderung wurde gesendet"
-#: ../src/roster_window.py:1718
+#: ../src/roster_window.py:1774
#, python-format
msgid "If \"%s\" accepts this request you will know his or her status."
msgstr "Wenn \"%s\" diese Anfrage akzeptiert, erfahren Sie dessen Status."
-#: ../src/roster_window.py:1730
+#: ../src/roster_window.py:1786
msgid "Authorization has been removed"
msgstr "Autorisierung wurde entfernt"
-#: ../src/roster_window.py:1731
+#: ../src/roster_window.py:1787
#, python-format
msgid "Now \"%s\" will always see you as offline."
msgstr "\"%s\" wird Sie nun immer als offline sehen."
-#: ../src/roster_window.py:1752
+#: ../src/roster_window.py:1808
+#, fuzzy
msgid ""
-"Gnome Keyring is installed but not correctly started\t\t\t\t\t\t\t\t"
-"(environment variable probably not correctly set)"
+"Gnome Keyring is installed but not \t\t\t\t\t\t\tcorrectly started "
+"(environment variable probably not \t\t\t\t\t\t\tcorrectly set)"
msgstr ""
"Gnome Keyring ist installiert, aber nicht korrekt gestartet\t\t\t\t\t\t\t\t"
"(Umgebungsvariablen wahrscheinlich falsch gesetzt)"
-#: ../src/roster_window.py:1772
+#: ../src/roster_window.py:1829
msgid "GPG is not usable"
msgstr "GPG ist nicht benutzbar"
-#. %s is the account name here
-#: ../src/roster_window.py:1773 ../src/common/connection_handlers.py:2217
-#: ../src/common/zeroconf/connection_zeroconf.py:174
-#, python-format
-msgid "You will be connected to %s without OpenPGP."
-msgstr "Sie werden ohne OpenPGP mit %s verbunden."
-
-#: ../src/roster_window.py:1933 ../src/roster_window.py:3087
+#: ../src/roster_window.py:1991 ../src/roster_window.py:3138
msgid "You are participating in one or more group chats"
msgstr "Sie nehmen an einem oder mehreren Gruppenchats teil"
-#: ../src/roster_window.py:1934 ../src/roster_window.py:3088
+#: ../src/roster_window.py:1992 ../src/roster_window.py:3139
msgid ""
"Changing your status to invisible will result in disconnection from those "
"group chats. Are you sure you want to go invisible?"
@@ -6288,15 +8617,15 @@ msgstr ""
"Wenn Sie Ihren Status auf unsichtbar setzen, werden sie von diesen "
"Gruppenchats getrennt. Sind Sie sicher, dass sie unsichtbar werden möchten?"
-#: ../src/roster_window.py:1960
+#: ../src/roster_window.py:2018
msgid "desync'ed"
msgstr ""
-#: ../src/roster_window.py:2083 ../src/roster_window.py:2331
+#: ../src/roster_window.py:2141 ../src/roster_window.py:2385
msgid "You have unread messages"
msgstr "Sie haben ungelesene Nachrichten"
-#: ../src/roster_window.py:2084
+#: ../src/roster_window.py:2142
msgid ""
"Messages will only be available for reading them later if you have history "
"enabled and contact is in your roster."
@@ -6304,16 +8633,16 @@ msgstr ""
"Nachrichten werden nur für das spätere Lesen verfügbar sein, wenn der "
"Verlauf aktiviert wurde und der Kontakt sich in der Kontaktliste befindet."
-#: ../src/roster_window.py:2332
+#: ../src/roster_window.py:2386
msgid "You must read them before removing this transport."
msgstr "Sie müssen sie alle lesen, bevor der Account entfernt wird."
-#: ../src/roster_window.py:2335
+#: ../src/roster_window.py:2389
#, python-format
msgid "Transport \"%s\" will be removed"
msgstr "Transport \"%s\" wird entfernt"
-#: ../src/roster_window.py:2336
+#: ../src/roster_window.py:2390
msgid ""
"You will no longer be able to send and receive messages from contacts using "
"this transport."
@@ -6321,11 +8650,11 @@ msgstr ""
"Sie können nun keine Nachrichten mehr mit Kontakten von diesem Transport "
"austauschen."
-#: ../src/roster_window.py:2339
+#: ../src/roster_window.py:2393
msgid "Transports will be removed"
msgstr "Transporte werden entfernt"
-#: ../src/roster_window.py:2344
+#: ../src/roster_window.py:2398
#, python-format
msgid ""
"You will no longer be able to send and receive messages to contacts from "
@@ -6335,51 +8664,51 @@ msgstr ""
"austauschen: %s"
#. it's jid
-#: ../src/roster_window.py:2510
+#: ../src/roster_window.py:2564
msgid "Rename Contact"
msgstr "Kontakt umbenennen"
-#: ../src/roster_window.py:2511
+#: ../src/roster_window.py:2565
#, python-format
msgid "Enter a new nickname for contact %s"
msgstr "Geben Sie einen Spitznamen für den Kontakt %s ein"
-#: ../src/roster_window.py:2518
+#: ../src/roster_window.py:2572
msgid "Rename Group"
msgstr "Gruppe umbenennen"
-#: ../src/roster_window.py:2519
+#: ../src/roster_window.py:2573
#, python-format
msgid "Enter a new name for group %s"
msgstr "Geben Sie einen neuen Namen für die Gruppe %s ein"
-#: ../src/roster_window.py:2572
+#: ../src/roster_window.py:2620
msgid "Remove Group"
msgstr "Gruppe entfernen"
-#: ../src/roster_window.py:2573
+#: ../src/roster_window.py:2621
#, python-format
msgid "Do you want to remove group %s from the roster?"
msgstr "Möchten Sie wirklich die Gruppe %s von Ihrer Kontaktliste entfernen?"
-#: ../src/roster_window.py:2574
+#: ../src/roster_window.py:2622
msgid "Remove also all contacts in this group from your roster"
msgstr "Auch alle Kontakte dieser Gruppe von Ihrer Kontaktliste entfernen"
-#: ../src/roster_window.py:2605
+#: ../src/roster_window.py:2653
msgid "Assign OpenPGP Key"
msgstr "OpenPGP-Schlüssel Zuweisen"
-#: ../src/roster_window.py:2606
+#: ../src/roster_window.py:2654
msgid "Select a key to apply to the contact"
msgstr "Weisen Sie dem Kontakt einen Schüssel zu"
-#: ../src/roster_window.py:2986
+#: ../src/roster_window.py:3036
#, python-format
msgid "Contact \"%s\" will be removed from your roster"
msgstr "Kontakt \"%s\" wird von ihrer Kontaktliste entfernt"
-#: ../src/roster_window.py:2990
+#: ../src/roster_window.py:3040
msgid ""
"By removing this contact you also remove authorization resulting in him or "
"her always seeing you as offline."
@@ -6388,7 +8717,7 @@ msgstr ""
"Ihren Status zu sehen, wodurch der Kontakt Sie nur noch als offline sehen "
"wird."
-#: ../src/roster_window.py:2995
+#: ../src/roster_window.py:3045
msgid ""
"By removing this contact you also by default remove authorization resulting "
"in him or her always seeing you as offline."
@@ -6397,17 +8726,17 @@ msgstr ""
"die Berechtigung Ihren Status zu sehen, wodurch der Kontakt Sie nur noch als "
"offline sehen wird."
-#: ../src/roster_window.py:2998
+#: ../src/roster_window.py:3048
msgid "I want this contact to know my status after removal"
msgstr ""
"Ich möchte, dass dieser Kontakt meinen Status auch nach dem Entfernen sieht"
#. several contact to remove at the same time
-#: ../src/roster_window.py:3002
+#: ../src/roster_window.py:3052
msgid "Contacts will be removed from your roster"
msgstr "Kontakte werden von Ihrer Kontaktliste entfernt"
-#: ../src/roster_window.py:3006
+#: ../src/roster_window.py:3056
#, python-format
msgid ""
"By removing these contacts:%s\n"
@@ -6417,22 +8746,22 @@ msgstr ""
"entziehen Sie ihnen auch die Berechtigung Ihren Status zu sehen, wodurch die "
"Kontakte Sie nur noch als offline sehen werden."
-#: ../src/roster_window.py:3044
+#: ../src/roster_window.py:3094
msgid "No account available"
msgstr "Kein Konto vorhanden"
-#: ../src/roster_window.py:3045
+#: ../src/roster_window.py:3095
msgid "You must create an account before you can chat with other contacts."
msgstr ""
"Sie müssen ein Konto erstellen, bevor Sie sich zum Jabber-Netzwerk verbinden "
"können."
-#: ../src/roster_window.py:3519
+#: ../src/roster_window.py:3582
msgid "Metacontacts storage not supported by your server"
msgstr ""
"Das Speichern von Metakontakten wird von Ihrem Server nicht unterstützt"
-#: ../src/roster_window.py:3521
+#: ../src/roster_window.py:3584
msgid ""
"Your server does not support storing metacontacts information. So those "
"information will not be saved on next reconnection."
@@ -6441,14 +8770,14 @@ msgstr ""
"Informationen. Aus diesem Grund werden diese Informationen bei der nächsten "
"Neuverbindung nicht gespeichert werden."
-#: ../src/roster_window.py:3596
+#: ../src/roster_window.py:3659
msgid ""
"You are about to create a metacontact. Are you sure you want to continue?"
msgstr ""
"Sie sind dabei einen Metakontakt zu erstellen. Wollen Sie wirklich "
"fortfahren?"
-#: ../src/roster_window.py:3598
+#: ../src/roster_window.py:3661
msgid ""
"Metacontacts are a way to regroup several contacts in one line. Generally it "
"is used when the same person has several Jabber accounts or transport "
@@ -6458,11 +8787,11 @@ msgstr ""
"gruppieren. Normalerweise benutzt man Sie, wenn eine Person mehrere Jabber- "
"oder Transport-Konten hat."
-#: ../src/roster_window.py:3705
+#: ../src/roster_window.py:3774
msgid "Invalid file URI:"
msgstr "Ungültige Datei URI:"
-#: ../src/roster_window.py:3716
+#: ../src/roster_window.py:3785
#, python-format
msgid "Do you want to send this file to %s:"
msgid_plural "Do you want to send those files to %s:"
@@ -6474,112 +8803,124 @@ msgstr[1] "Wollen Sie die Dateien an %s senden:"
#. for chat_with
#. for single message
#. join gc
-#: ../src/roster_window.py:4226 ../src/roster_window.py:4236
-#: ../src/roster_window.py:4245 ../src/systray.py:212 ../src/systray.py:217
+#: ../src/roster_window.py:4298 ../src/roster_window.py:4308
+#: ../src/roster_window.py:4317 ../src/systray.py:212 ../src/systray.py:217
#: ../src/systray.py:223
#, python-format
msgid "using account %s"
msgstr "mit Konto %s"
#. add
-#: ../src/roster_window.py:4252
+#: ../src/roster_window.py:4324
#, python-format
msgid "to %s account"
msgstr "an Konto %s"
#. disco
-#: ../src/roster_window.py:4257
+#: ../src/roster_window.py:4329
#, python-format
msgid "using %s account"
msgstr "mit Konto %s"
-#: ../src/roster_window.py:4338
+#: ../src/roster_window.py:4410
msgid "_Manage Bookmarks..."
msgstr "Lesezeichen _verwalten ..."
#. profile, avatar
-#: ../src/roster_window.py:4357
+#: ../src/roster_window.py:4429
#, python-format
msgid "of account %s"
msgstr "von Konto %s"
-#: ../src/roster_window.py:4397
+#: ../src/roster_window.py:4469
#, python-format
msgid "for account %s"
msgstr "für Konto %s"
-#: ../src/roster_window.py:4451 ../src/roster_window.py:4558
+#: ../src/roster_window.py:4523 ../src/roster_window.py:4637
msgid "_Change Status Message"
msgstr "Ändere _Statusnachricht"
-#: ../src/roster_window.py:4489
+#: ../src/roster_window.py:4554
#, fuzzy
+msgid "Publish Tune"
+msgstr "_Veröffentliche Musiktitel"
+
+#: ../src/roster_window.py:4559
+msgid "Mood"
+msgstr ""
+
+#: ../src/roster_window.py:4563
+msgid "Activity"
+msgstr "Aktivität"
+
+#: ../src/roster_window.py:4568
msgid "Configure Services..."
-msgstr "Konfigurieren ..."
+msgstr "Dienste konfigurieren ..."
-#: ../src/roster_window.py:4647
+#: ../src/roster_window.py:4726
msgid "_Maximize All"
msgstr "Alle _maximieren"
#. Send Group Message
-#: ../src/roster_window.py:4655 ../src/roster_window.py:5137
+#: ../src/roster_window.py:4734 ../src/roster_window.py:5219
msgid "Send Group M_essage"
msgstr "_Sende Nachricht an Gruppe"
-#: ../src/roster_window.py:4663
+#: ../src/roster_window.py:4742
msgid "To all users"
msgstr "An alle Benutzern"
-#: ../src/roster_window.py:4666
+#: ../src/roster_window.py:4746
msgid "To all online users"
msgstr "An alle angemeldeten Benutzer"
-#: ../src/roster_window.py:5054
+#: ../src/roster_window.py:5135
msgid "I would like to add you to my roster"
msgstr "Ich würde dich gerne in meine Liste aufnehmen"
#. Manage Transport submenu
-#: ../src/roster_window.py:5157
+#: ../src/roster_window.py:5239
msgid "_Manage Contacts"
msgstr "Kontakte verwalten"
#. Send single message
-#: ../src/roster_window.py:5218
+#: ../src/roster_window.py:5300
msgid "Send Single Message"
msgstr "Sende _einzelne Nachricht"
#. Manage Transport submenu
-#: ../src/roster_window.py:5273
+#: ../src/roster_window.py:5356
msgid "_Manage Transport"
msgstr "Transports"
#. Modify Transport
-#: ../src/roster_window.py:5281
+#: ../src/roster_window.py:5364
msgid "_Modify Transport"
msgstr "Trans_port ändern"
-#: ../src/roster_window.py:5356
+#: ../src/roster_window.py:5439
msgid "_Maximize"
msgstr "_Maximieren"
-#: ../src/roster_window.py:5363
+#: ../src/roster_window.py:5446
msgid "_Disconnect"
msgstr "_Verbindung trennen"
-#: ../src/roster_window.py:5440
+#: ../src/roster_window.py:5523
msgid "_New Group Chat"
msgstr "Neuer Gruppenchat"
#. History manager
-#: ../src/roster_window.py:5540
+#: ../src/roster_window.py:5623
msgid "History Manager"
msgstr "_Verlaufsmanager"
-#: ../src/roster_window.py:5549
+#: ../src/roster_window.py:5632
msgid "_Join New Group Chat"
msgstr "_Gruppenchat betreten"
-#: ../src/roster_window.py:5768
+#: ../src/roster_window.py:5840
msgid "Change Status Message..."
msgstr "Ändere Statusnachricht ..."
@@ -6596,6 +8937,35 @@ msgstr "Fehler den empfangenen Daten"
msgid "No result"
msgstr "Kein Ergebnis"
+#: ../src/secrets.py:45
+msgid ""
+"To continue, Gajim needs to access your stored secrets. Enter your passphrase"
+msgstr ""
+"Um fortzufahren benötigt Gajim Zugriff auf Ihre gespeicherten Passwörter. "
+"Geben Sie Ihre Passphrase ein"
+
+#: ../src/secrets.py:89
+msgid "Confirm Passphrase"
+msgstr "Passphrase bestätigen"
+
+#: ../src/secrets.py:90
+msgid "Enter your new passphrase again for confirmation"
+msgstr "Passphrase zur Bestätigung erneut eingeben"
+
+#: ../src/secrets.py:95 ../src/secrets.py:107
+msgid "Create Passphrase"
+msgstr "Passphrase erstellen"
+
+#: ../src/secrets.py:96
+msgid "Passphrases did not match.\n"
+msgstr "Passphrasen stimmen nicht überein.\n"
+
+#: ../src/secrets.py:97 ../src/secrets.py:108
+msgid "Gajim needs you to create a passphrase to encrypt stored secrets"
+msgstr ""
+"Sie müssen eine Passphrase angeben um Ihre gespeicherten Passwörter zu "
+"verschlüsseln"
+
#: ../src/systray.py:169
msgid "_Change Status Message..."
msgstr "Ändere _Statusnachricht ..."
@@ -6604,11 +8974,11 @@ msgstr "Ändere _Statusnachricht ..."
msgid "Hide this menu"
msgstr "Versteckt dieses Menü"
-#: ../src/tooltips.py:317 ../src/tooltips.py:566
+#: ../src/tooltips.py:317 ../src/tooltips.py:512
msgid "Jabber ID: "
msgstr "Jabber-ID:"
-#: ../src/tooltips.py:320 ../src/tooltips.py:570
+#: ../src/tooltips.py:320 ../src/tooltips.py:516
msgid "Resource: "
msgstr "Ressource: "
@@ -6617,62 +8987,71 @@ msgstr "Ressource: "
msgid "%(owner_or_admin_or_member)s of this group chat"
msgstr "%(owner_or_admin_or_member)s dieses Gruppenchats"
-#: ../src/tooltips.py:423
+#: ../src/tooltips.py:422
msgid " [blocked]"
msgstr " [blockiert]"
-#: ../src/tooltips.py:427
+#: ../src/tooltips.py:426
msgid " [minimized]"
msgstr " [minimiert]"
-#: ../src/tooltips.py:442 ../src/tooltips.py:686
+#: ../src/tooltips.py:441 ../src/tooltips.py:683
msgid "Status: "
msgstr "Status: "
-#: ../src/tooltips.py:476
+#: ../src/tooltips.py:472
#, python-format
msgid "Last status: %s"
msgstr "Letzter Status: %s"
-#: ../src/tooltips.py:478
+#: ../src/tooltips.py:474
#, python-format
msgid " since %s"
msgstr " seit %s"
-#: ../src/tooltips.py:496
+#: ../src/tooltips.py:492
msgid "Connected"
msgstr "Verbunden"
-#: ../src/tooltips.py:498
+#: ../src/tooltips.py:494
msgid "Disconnected"
msgstr "Nicht verbunden"
-#: ../src/tooltips.py:507
+#. ('both' is the normal sub so we don't show it)
+#: ../src/tooltips.py:523
+msgid "Subscription: "
+msgstr "Abonnement: "
+
+#: ../src/tooltips.py:533
+msgid "OpenPGP: "
+msgstr "OpenPGP: "
+
+#: ../src/tooltips.py:576
msgid "Mood:"
msgstr "Stimmung:"
-#: ../src/tooltips.py:519
+#: ../src/tooltips.py:586
msgid "Activity:"
msgstr "Aktivität:"
-#: ../src/tooltips.py:539
+#: ../src/tooltips.py:604
msgid "Unknown Artist"
msgstr "Unbekannter Künstler"
-#: ../src/tooltips.py:544
+#: ../src/tooltips.py:609
msgid "Unknown Title"
msgstr "Unbekannter Titel"
-#: ../src/tooltips.py:549
+#: ../src/tooltips.py:614
msgid "Unknown Source"
msgstr "Unbekannte Quelle"
-#: ../src/tooltips.py:550
+#: ../src/tooltips.py:615
#, fuzzy
msgid "Tune:"
msgstr "Musiktitel:"
-#: ../src/tooltips.py:550
+#: ../src/tooltips.py:615
#, python-format
msgid ""
"<b>\"%(title)s\"</b> by <i>%(artist)s</i>\n"
@@ -6681,61 +9060,52 @@ msgstr ""
"<b>\"%(title)s\"</b> von <i>%(artist)s</i>\n"
"von <i>%/source)s</i>"
-#. ('both' is the normal sub so we don't show it)
-#: ../src/tooltips.py:577
-msgid "Subscription: "
-msgstr "Abonnement: "
-
-#: ../src/tooltips.py:587
-msgid "OpenPGP: "
-msgstr "OpenPGP: "
-
-#: ../src/tooltips.py:642
+#: ../src/tooltips.py:639
msgid "Download"
msgstr "Download"
-#: ../src/tooltips.py:648
+#: ../src/tooltips.py:645
msgid "Upload"
msgstr "Upload"
-#: ../src/tooltips.py:655
+#: ../src/tooltips.py:652
msgid "Type: "
msgstr "Typ: "
-#: ../src/tooltips.py:661
+#: ../src/tooltips.py:658
msgid "Transferred: "
msgstr "Ãœbertragen: "
-#: ../src/tooltips.py:664 ../src/tooltips.py:685
+#: ../src/tooltips.py:661 ../src/tooltips.py:682
msgid "Not started"
msgstr "Nicht gestartet"
-#: ../src/tooltips.py:668
+#: ../src/tooltips.py:665
msgid "Stopped"
msgstr "Angehalten"
-#: ../src/tooltips.py:670 ../src/tooltips.py:673
+#: ../src/tooltips.py:667 ../src/tooltips.py:670
msgid "Completed"
msgstr "Abgeschlossen"
-#: ../src/tooltips.py:677
+#: ../src/tooltips.py:674
msgid "?transfer status:Paused"
msgstr "?Transferstatus:Pausiert"
#. stalled is not paused. it is like 'frozen' it stopped alone
-#: ../src/tooltips.py:681
+#: ../src/tooltips.py:678
msgid "Stalled"
msgstr "Steht still"
-#: ../src/tooltips.py:683
+#: ../src/tooltips.py:680
msgid "Transferring"
msgstr "Ãœbertrage"
-#: ../src/tooltips.py:719
+#: ../src/tooltips.py:716
msgid "This service has not yet responded with detailed information"
msgstr "Dieser Dienst hat nicht mit detaillierten Informationen geantwortet"
-#: ../src/tooltips.py:722
+#: ../src/tooltips.py:719
msgid ""
"This service could not respond with detailed information.\n"
"It is most likely legacy or broken"
@@ -6804,2315 +9174,26 @@ msgstr ""
msgid " resource with priority "
msgstr " resource mit Priorität "
-#: ../src/common/check_paths.py:38
-msgid "creating logs database"
-msgstr "Erstelle Log-Datenbank"
-
-#: ../src/common/check_paths.py:105 ../src/common/check_paths.py:116
-#: ../src/common/check_paths.py:123
-#, python-format
-msgid "%s is a file but it should be a directory"
-msgstr "%s ist eine Datei, aber es sollte ein Verzeichnis sein"
-
-#: ../src/common/check_paths.py:106 ../src/common/check_paths.py:117
-#: ../src/common/check_paths.py:124 ../src/common/check_paths.py:132
-msgid "Gajim will now exit"
-msgstr "Gajim wird nun beendet"
-
-#: ../src/common/check_paths.py:131
-#, python-format
-msgid "%s is a directory but should be a file"
-msgstr "%s ist ein Verzeichnis, sollte aber eine Datei sein"
-
-#: ../src/common/check_paths.py:147
-#, python-format
-msgid "creating %s directory"
-msgstr "Erstelle Verzeichnis %s"
-
-#: ../src/common/commands.py:74
-msgid "Change status information"
-msgstr "Ändere Statusinformation"
-
-#: ../src/common/commands.py:87
-msgid "Change status"
-msgstr "Kontakt hat Status verändert"
-
-#: ../src/common/commands.py:88
-msgid "Set the presence type and description"
-msgstr "Lege den Anwesenheitstyp und die Beschreibung fest"
-
-#: ../src/common/commands.py:94
-msgid "Free for chat"
-msgstr "Frei zum Chatten"
-
-#: ../src/common/commands.py:95
-msgid "Online"
-msgstr "Online"
-
-#: ../src/common/commands.py:97
-msgid "Extended away"
-msgstr "Längere Zeit abwesend"
-
-#: ../src/common/commands.py:98
-msgid "Do not disturb"
-msgstr "Bitte nicht stören"
-
-#: ../src/common/commands.py:99
-msgid "Offline - disconnect"
-msgstr "Offline - Abgemeldet"
-
-#: ../src/common/commands.py:104
-msgid "Presence description:"
-msgstr "Anwesenheitsbeschreibung:"
-
-#: ../src/common/commands.py:139
-msgid "The status has been changed."
-msgstr "Der Status hat sich verändert."
-
-#: ../src/common/commands.py:170 ../src/common/commands.py:194
-msgid "Leave Groupchats"
-msgstr "Verlasse Gruppenchat"
-
-#: ../src/common/commands.py:184
-#, python-format
-msgid "%(nickname)s on %(room_jid)s"
-msgstr "%(nickname)s aus Gruppenchat %(room_jid)s"
-
-#: ../src/common/commands.py:188
-msgid "You have not joined a groupchat."
-msgstr "Sie haben keinen Gruppenchat betreten."
-
-#: ../src/common/commands.py:195
-msgid "Choose the groupchats you want to leave"
-msgstr "Wählen Sie den Gruppenchat, den Sie verlassen möchten"
-
-#: ../src/common/commands.py:235
-msgid "You left the following groupchats:"
-msgstr "Sie haben folgende Gruppenchats verlassen:"
-
-#: ../src/common/commands.py:247
-msgid "Forward unread messages"
-msgstr "Ungelesene Nachrichten weiterleiten"
-
-#: ../src/common/commands.py:267
-msgid "All unread messages have been forwarded."
-msgstr "Alle ungelesenen Nachrichten wurden weitergeleitet."
-
-#: ../src/common/config.py:74
-msgid "Use D-Bus and Notification-Daemon to show notifications"
-msgstr ""
-"Verwende D-Bus und Notification-Daemon, um Benachrichtigungen zu zeigen"
-
-#: ../src/common/config.py:79
-msgid "Time in minutes, after which your status changes to away."
-msgstr "Zeit in Minuten, nach der Ihr Status auf abwesend gesetzt wird"
-
-#: ../src/common/config.py:80
-msgid "$S (Away as a result of being idle more than $T min)"
-msgstr "$S (Abwesend wegen Untätigkeit für mehr als $T Minuten)"
-
-#: ../src/common/config.py:80
-msgid "$S will be replaced by current status message, $T by autoaway time."
-msgstr ""
-"$S wird mit der aktuellen Statusnachricht ersetzt, $T durch die "
-"Abwesenheitszeit."
-
-#: ../src/common/config.py:82
-msgid "Time in minutes, after which your status changes to not available."
-msgstr "Zeit in Minuten, nach der Ihr Status auf nicht verfügbar gesetzt wird."
-
-#: ../src/common/config.py:83
-msgid "$S (Not available as a result of being idle more than $T min)"
-msgstr "$S (Nicht verfügbar wegen Untätigkeit seit mehr als $T Minuten)"
-
-#: ../src/common/config.py:83
-msgid "$S will be replaced by current status message, $T by autoxa time."
-msgstr ""
-"$S wird mit der aktuellen Statusnachricht ersetzt, $T durch die Nicht-"
-"Verfügbar-Zeit."
-
-#: ../src/common/config.py:101
-msgid ""
-"List (space separated) of rows (accounts and groups) that are collapsed."
-msgstr ""
-"Liste (leerzeichengeteilt) von Reihen (Kontos und Gruppen), die eingeklappt "
-"sind."
-
-#: ../src/common/config.py:105
-msgid "Enable link-local/zeroconf messaging"
-msgstr "Aktiviere Anzeige von Kontakten im LAN über Zeroconf."
-
-#: ../src/common/config.py:108
-msgid "Language used by speller"
-msgstr "Sprache der Rechtschreibprüfung"
-
-#: ../src/common/config.py:109
-msgid ""
-"'always' - print time for every message.\n"
-"'sometimes' - print time every print_ichat_every_foo_minutes minute.\n"
-"'never' - never print time."
-msgstr ""
-"'always' - Zeige Zeit für jede Nachricht.\n"
-"'sometimes' - Zeige Zeit alle print_ichat_every_foo_minutes Minuten.\n"
-"'never' - Zeige nie die Zeit."
-
-#: ../src/common/config.py:110
-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 least precise one. "
-"This is used only if print_time is 'sometimes'."
-msgstr ""
-"Zeige die Zeit in Chats mittels ungenauer Uhr an. Werte für die "
-"Ungenauigkeit sind 1 bis 4 oder 0, um die ungenaue Uhr zu deaktivieren. 1 "
-"ist am Genauesten, 4 ist am Ungenaueste. Dies wird nur verwendet, wenn "
-"print_time auf 'sometimes' gesetzt ist."
-
-#: ../src/common/config.py:113
-msgid "Treat * / _ pairs as possible formatting characters."
-msgstr "Behandle * / _ Paare als mögliche Formatierungszeichen."
-
-#: ../src/common/config.py:114
-msgid ""
-"If True, do not remove */_ . So *abc* will be bold but with * * not removed."
-msgstr ""
-"Wenn aktiviert, wird */_ nicht entfernt. So wird *abc* fett gedruckt, aber * "
-"* wird nicht entfernt."
-
-#: ../src/common/config.py:117
-msgid ""
-"Uses ReStructured text markup to send HTML, plus ascii formatting if "
-"selected. For syntax, see http://docutils.sourceforge.net/docs/ref/rst/"
-"restructuredtext.html (If you want to use this, install docutils)"
-msgstr ""
-"Wenn aktiviert, wird reStructuredText für die HTML und ASCII-Formatierung "
-"verwendet (falls dies erwünscht ist, installieren Sie docutils). "
-"Informationen zur Syntax finden Sie unter http://docutils.sourceforge.net/"
-"docs/ref/rst/restructuredtext.html (Englisch)"
-
-#: ../src/common/config.py:126
-msgid ""
-"Character to add after nickname when using nick completion (tab) in group "
-"chat."
-msgstr ""
-"Zeichen, das hinter Spitznamen angezeigt wird, wenn "
-"Spitznamenvervollständigung (Tab) im Gruppenchat verwendet wird."
-
-#: ../src/common/config.py:127
-msgid ""
-"Character to propose to add after desired nickname when desired nickname is "
-"used by someone else in group chat."
-msgstr ""
-"Zeichen, das hinter den gewünschten Spitznamen gehängt wird, falls der "
-"gewünschteName im Gruppenchat bereits vergeben ist."
-
-#: ../src/common/config.py:150
-msgid ""
-"This option let you customize timestamp that is printed in conversation. For "
-"exemple \"[%H:%M] \" will show \"[hour:minute] \". See python doc on "
-"strftime for full documentation: http://docs.python.org/lib/module-time.html"
-msgstr ""
-"Hiermit lässt sich der Zeitstempel anpassen, der in den Unterhaltungen "
-"verwendet wird. Zum Beispiel wird \"[%H:%M] \" zu \"[Stunde:Minute] \". "
-"Weitere Informationen finden Sie in der Python-Dokumentation zu 'strftime' "
-"unter http://docs.python.org/lib/module-time.html (Englisch)."
-
-#: ../src/common/config.py:151
-msgid "Characters that are printed before the nickname in conversations"
-msgstr "Zeichen, die vor dem Spitzname gedruckt werden"
-
-#: ../src/common/config.py:152
-msgid "Characters that are printed after the nickname in conversations"
-msgstr "Zeichen, die nach dem Spitznamen gedruckt werden"
-
-#: ../src/common/config.py:154
-msgid "The username used to identify the Last.fm account."
-msgstr "Benutzername zur Identifikation des Last.FM Kontos."
-
-#: ../src/common/config.py:158
-msgid "Add * and [n] in roster title?"
-msgstr "Füge * und [n] in die Kontaktleiste?"
-
-#: ../src/common/config.py:159
-msgid ""
-"How many lines to remember from previous conversation when a chat tab/window "
-"is reopened."
-msgstr ""
-"Wie viele Zeilen von der vorherigen Unterhaltung gespeichert werden, wenn "
-"ein Chat Tab/Fenster wieder geöffnet wird."
-
-#: ../src/common/config.py:160
-msgid "How many minutes should last lines from previous conversation last."
-msgstr ""
-"Wie viele Minuten die Zeilen vom vorherigen Chat angezeigt werden sollen."
-
-#: ../src/common/config.py:161
-msgid ""
-"Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ "
-"Client default behaviour)."
-msgstr ""
-"Sende Nachricht mit Strg+Enter und mache bei Enter einen Zeilenumbruch "
-"(Mirabilis ICQ-Client Standardeinstellung)."
-
-#: ../src/common/config.py:163
-msgid "How many lines to store for Ctrl+KeyUP."
-msgstr "Wie viele Zeilen für Strg+BildAuf gespeichert werden."
-
-#: ../src/common/config.py:166
-#, python-format
-msgid ""
-"Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' "
-"which means use wiktionary."
-msgstr ""
-"Entweder eine spezielle URL mit %s, wobei %s das Word/Phrase ist oder "
-"'WIKTIONARY', was bedeutet, dass das Wörterbuch Wiktionary verwendet wird."
-
-#: ../src/common/config.py:169
-msgid "If checked, Gajim can be controlled remotely using gajim-remote."
-msgstr ""
-"Falls aktiviert, kann Gajim von Außerhalb mittels gajim-remote kontrolliert "
-"werden."
-
-#: ../src/common/config.py:170
-msgid ""
-"If True, listen to D-Bus signals from NetworkManager and change the status "
-"of accounts (provided they do not have listen_to_network_manager set to "
-"False and they sync with global status) based upon the status of the network "
-"connection."
-msgstr ""
-"Wenn aktiviert, reagiere auf D-Bus-Signale vom NetworkManager und ändere den "
-"Status von Konten (vorausgesetzt, sie haben listen_to_network_manager nicht "
-"auf False gesetzt und sie synchronisieren sich mit dem globalen Status) "
-"basierend auf dem Status der Netzwerkverbindung."
-
-#: ../src/common/config.py:171
-msgid ""
-"Sent chat state notifications. Can be one of all, composing_only, disabled."
-msgstr ""
-"Chat-Statusbenachrichtigungen gesendet. Kann eine von allen sein, "
-"composing_only deaktiviert."
-
-#: ../src/common/config.py:172
-msgid ""
-"Displayed chat state notifications in chat windows. Can be one of all, "
-"composing_only, disabled."
-msgstr ""
-"Angezeigte Chat-Status Benachrichtigungen im Chatfenster. Kann all, "
-"composing_only oder disabled sein."
-
-#: ../src/common/config.py:174
-msgid ""
-"When not printing time for every message (print_time==sometimes), print it "
-"every x minutes."
-msgstr ""
-"Wenn die Uhrzeit nicht für jede Nachricht angezeigt werden soll "
-"(print_time==sometimes), zeige sie alle x Minuten."
-
-#: ../src/common/config.py:175
-msgid "Ask before closing a group chat tab/window."
-msgstr "Fragen, bevor ein Gruppenchat Tab/Fenster geschlossen wird."
-
-#: ../src/common/config.py:176
-msgid ""
-"Always ask before closing group chat tab/window in this space separated list "
-"of group chat jids."
-msgstr ""
-"Immer fragen, bevor ein Gruppenchat-Fenster/Tab aus der Leerzeichen-"
-"seperierten Liste von Gruppchatnamen geschlossen wird."
-
-#: ../src/common/config.py:177
-msgid ""
-"Never ask before closing group chat tab/window in this space separated list "
-"of group chat jids."
-msgstr ""
-"Niemals fragen, bevor ein Gruppenchat-Fenster/Tab aus der Leerzeichen-"
-"seperierten Liste von Gruppenchatnamen geschlossen wird."
-
-#: ../src/common/config.py:180
-#, fuzzy
-msgid ""
-"Comma separated list of hosts that we send, in addition of local interfaces, "
-"for File Transfer in case of address translation/port forwarding."
-msgstr ""
-"Überschreibt den Host für Datenübertragung für den Fall von NAT/Port-"
-"Forwarding"
-
-#: ../src/common/config.py:182
-msgid "IEC standard says KiB = 1024 bytes, KB = 1000 bytes."
-msgstr "IEC-Standard sagt KiB = 1024 Byte, kB = 1000 Byte."
-
-#: ../src/common/config.py:184
-msgid "Notify of events in the system trayicon."
-msgstr "Benachrichtige über Ereignisse durch das Trayicon"
-
-#: ../src/common/config.py:190
-msgid "Show tab when only one conversation?"
-msgstr "Tab bei einer einzelnen Unterhaltung anzeigen?"
-
-#: ../src/common/config.py:191
-msgid "Show tabbed notebook border in chat windows?"
-msgstr "Tab-Grenze im Chat-Fenster anzeigen?"
-
-#: ../src/common/config.py:192
-msgid "Show close button in tab?"
-msgstr "Schließen-Button im Tab anzeigen?"
-
-#: ../src/common/config.py:193
-msgid ""
-"When negotiating an encrypted session, should Gajim assume you want your "
-"messages to be logged?"
-msgstr ""
-"Soll Gajim beim Aushandeln einer verschlüsselten Sitzung annehmen, dass Sie "
-"Ihre Nachricht mitschneiden wollen?"
-
-#: ../src/common/config.py:194
-msgid ""
-"When negotiating an encrypted session, should Gajim prefer to use public "
-"keys for identification?"
-msgstr ""
-"Soll Gajim bei Aushandeln einer verschlüsselten Situng öffentliche Schlüssel "
-"zur Identifikation bevorzugen?"
-
-#: ../src/common/config.py:203
-msgid "Preview new messages in notification popup?"
-msgstr "Vorschau neuer Nachrichten im Benachrichtigungs-Popup?"
-
-#: ../src/common/config.py:208
-msgid ""
-"A semicolon-separated list of words that will be highlighted in group chats."
-msgstr ""
-"Eine mit Semilkolon seperierte Liste von Worten, die in einem Gruppenchat "
-"hervorgehoben werden."
-
-#: ../src/common/config.py:209
-msgid ""
-"If True, quits Gajim when X button of Window Manager is clicked. This "
-"setting is taken into account only if trayicon is used."
-msgstr ""
-"Falls aktiviert, wird Gajim beendet, wenn der X-Button des Fenstermanagers "
-"geklickt wird. Diese Option wird nur beachtet, wenn die Trayicon-"
-"Unterstützung aktiv ist."
-
-#: ../src/common/config.py:210
-msgid ""
-"If True, Gajim will check if it's the default jabber client on each startup."
-msgstr ""
-"Wenn aktiviert, wird Gajim beim Starten überprüfen, ob Gajim der Standard-"
-"Jabber-Client ist."
-
-#: ../src/common/config.py:211
-msgid ""
-"If True, Gajim will display an icon on each tab containing unread messages. "
-"Depending on the theme, this icon may be animated."
-msgstr ""
-"Falls aktiviert, wird Gajim ein Icon auf jedem Tab mit ungelesenen "
-"Nachrichten anzeigen. Abhängig vom Thema kann dieses Icon animiert sein."
-
-#: ../src/common/config.py:212
-msgid ""
-"If True, Gajim will display the status message, if not empty, for every "
-"contact under the contact name in roster window."
-msgstr ""
-"Falls aktiviert, wird Gajim für jeden Kontakt die Statusnachricht, falls "
-"nicht leer, unter dem jeweiligen Kontaktnamen im Roster anzeigen."
-
-#: ../src/common/config.py:214
-msgid "Define the position of the avatar in roster. Can be left or right"
-msgstr ""
-"Definiere die Position des Avatars in der Kontaktliste. Links oder rechts "
-"sind mögliche Optionen."
-
-#: ../src/common/config.py:215
-msgid ""
-"If True, Gajim will ask for avatar each contact that did not have an avatar "
-"last time or has one cached that is too old."
-msgstr ""
-"Falls aktiviert fragt Gajim nach einem Avatar für jeden Kontakt, der beim "
-"letzten Mal keinen hatte oder einen veralteten hat."
-
-#: ../src/common/config.py:216
-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 ""
-"Falls deaktiviert, werden Sie keine Statuszeile im Chat sehen, wenn ein "
-"Kontakt seinen Status und/oder seine Statusnachricht ändert."
-
-#: ../src/common/config.py:217
-msgid ""
-"can be \"none\", \"all\" or \"in_and_out\". If \"none\", Gajim will no "
-"longer print status line in groupchats when a member changes his or her "
-"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 ""
-"Kann \"none\", \"all\" oder \"in_and_out\" sein. Falls \"none\", wird Gajim "
-"keine Status-Zeilen mehr in Gruppenchats anzeigen, wenn ein Mitglied seinen "
-"Status oder seine Statusnachricht ändert. Falls \"all\", wird Gajim alle "
-"Statusnachrichten anzeigen. Falls \"in_and_out\", wird Gajim nur einen "
-"Status anzeigen, wenn jemand den Gruppenchat betritt oder verlässt."
-
-#: ../src/common/config.py:219
-msgid "Background color of contacts when they just signed in."
-msgstr "Hintergrundfarbe von Kontakten wenn sie sich gerade noch anmeldeten."
-
-#: ../src/common/config.py:220
-msgid "Background color of contacts when they just signed out."
-msgstr "Hintergrundfarbe von Kontakten wenn sie sich gerade noch abmeldeten."
-
-#: ../src/common/config.py:222
-msgid ""
-"If True, restored messages will use a smaller font than the default one."
-msgstr ""
-"Falls aktiv, werden wiederhergestellte Nachrichten mit einer kleineren "
-"Schrift als der Standard dargestellt."
-
-#: ../src/common/config.py:223
-msgid "Don't show avatar for the transport itself."
-msgstr "Zeige keinen Avatar für den Transport selber."
-
-#: ../src/common/config.py:224
-msgid "Don't show roster in the system taskbar."
-msgstr "Kontaktliste nicht in der System-Taskleiste anzeigen."
-
-#: ../src/common/config.py:225
-msgid ""
-"If True and installed GTK+ and PyGTK versions are at least 2.8, make the "
-"window flash (the default behaviour in most Window Managers) when holding "
-"pending events."
-msgstr ""
-"Wenn aktiviert und die installierten GTK+ und PyGTK Versionen wenigstens 2.8 "
-"sind, lasse das Fenster bei neuen Ereignissen aufblitzen (Standardverhalten "
-"in den meisten Window-Managers) "
-
-#: ../src/common/config.py:227
-msgid ""
-"Jabberd1.4 does not like sha info when one join a password protected group "
-"chat. Turn this option to False to stop sending sha info in group chat "
-"presences."
-msgstr ""
-"Jabberd1.4 mag keine SHA-Informationen wenn ein passwortgeschützter "
-"Gruppenchat betreten wird. Deaktivieren Sie diese Option um keine SHA-"
-"Informationen in Gruppenchats zu senden"
-
-#. always, never, peracct, pertype should not be translated
-#: ../src/common/config.py:230
-msgid ""
-"Controls the window where new messages are placed.\n"
-"'always' - All messages are sent to a single window.\n"
-"'always_with_roster' - Like 'always' but the messages are in a single window "
-"along with the roster.\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."
-msgstr ""
-"Kontrolliert das Fenster in dem neue Nachrichten platziert werden.\n"
-"'Immer' - Alle Nachrichten landen in einem einzelnen Fenster.\n"
-"'Immer im Kontaktfenster' - Wie 'Immer', abe rdie Nachrichten sind in einem "
-"einzelnen Fenster zusammen mit der Kontaktliste.\n"
-"'Niemals' - Alle Nachrichten bekommen ihr eigenes Fenster.\n"
-"'Pro Konto' - Nachrichten für jedes Konto landen in einem speziellen "
-"Fenster.\n"
-"'Pro Typ' - Jede Nachrichtentyp (z.B. Chat vs. Gruppenchat) landet in einem "
-"speziellen Fenster. Falls Sie diese Option ändern, muss Gajim neu gestartet "
-"werden, damit die Änderungen übernommen werden"
-
-#: ../src/common/config.py:231
-msgid "If False, you will no longer see the avatar in the chat window."
-msgstr ""
-"Wenn deaktiviert, werden Sie den Avatar nicht mehr im Chatfenster sehen."
-
-#: ../src/common/config.py:232
-msgid "If True, pressing the escape key closes a tab/window."
-msgstr "Wenn aktiviert, schließt die Escape-Taste ein Tab/Fenster."
-
-#: ../src/common/config.py:233
-msgid "Hides the buttons in chat windows."
-msgstr "Versteckt die Knöpfe im Gruppenchat-Fenster."
-
-#: ../src/common/config.py:234
-msgid "Hides the banner in a group chat window"
-msgstr "Versteckt das Banner im Gruppenchat-Fenster"
-
-#: ../src/common/config.py:235
-msgid "Hides the banner in two persons chat window"
-msgstr "Versteckt das Banner im Zwei-Personen-Chatfenster"
-
-#: ../src/common/config.py:236
-msgid "Hides the group chat occupants list in group chat window."
-msgstr "Versteckt die Gruppenchat-Benutzerliste im Gruppenchat-Fenster."
-
-#: ../src/common/config.py:237
-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 ""
-"Zeige in einem Chat den Spitznamen nur dann am Beginn einer Linie, wenn es "
-"nicht die selbe Person ist die schon die letzte Nachricht geschrieben hat."
-
-#: ../src/common/config.py:238
-msgid "Indentation when using merge consecutive nickname."
-msgstr ""
-"Einrückung, wenn das Zusammenführen nachfolgender Spitznamen verwendet wird."
-
-#: ../src/common/config.py:239
-msgid "Smooth scroll message in conversation window"
-msgstr "Sanftes Scrollen der Nachricht im Unterhaltungsfenster"
-
-#: ../src/common/config.py:240
-msgid "List of colors that will be used to color nicknames in group chats."
-msgstr ""
-"Farbliste, die für die Einfärbung von Spitzenamen im Gruppenchat verwendet "
-"wird."
-
-#: ../src/common/config.py:241
-msgid "Ctrl-Tab go to next composing tab when none is unread."
-msgstr ""
-"Strg-Tab, um zum nächsten Reiter zu gehen, wenn kein Anderer neue "
-"Nachrichten enthält."
-
-#: ../src/common/config.py:242
-msgid ""
-"Should we show the confirm metacontacts creation dialog or not? Empty string "
-"means we never show the dialog."
-msgstr ""
-"Soll der Bestätigungsdialog beim Erstellen von Metakontakten angezeigt "
-"werden oder nicht? Bei leerer Zeichenfolge wird der Dialog nie gezeigt."
-
-#: ../src/common/config.py:243
-msgid ""
-"If True, you will be able to set a negative priority to your account in "
-"account modification window. BE CAREFUL, when you are logged in with a "
-"negative priority, you will NOT receive any message from your server."
-msgstr ""
-"Wenn aktiviert, werden Sie in der Lage sein eine negative Priorität für Ihr "
-"Konto im Konto-Bearbeiten Fenster zu setzen. SEIEN SIE VORSICHTIG, wenn Sie "
-"mit einer negativen Priorität angemeldet sind: Sie werden KEINE Nachrichten "
-"mehr von Ihrem Server erhalten."
-
-#: ../src/common/config.py:244
-msgid ""
-"If True, Gajim will use Gnome Keyring (if available) to store account "
-"passwords."
-msgstr ""
-"Wenn aktiviert, wird Gnome Keyring (falls verfügbar) verwendet, um die "
-"Passwörter der Konten zu speichern."
-
-#: ../src/common/config.py:245
-msgid ""
-"If True, Gajim will show number of online and total contacts in account and "
-"group rows."
-msgstr ""
-"Wenn aktiviert, wird Gajim die Anzahl der angemeldeten und gesamten Kontakte "
-"in den Konto- und Gruppenreihen anzeigen."
-
-#: ../src/common/config.py:246
-msgid ""
-"Can be empty, 'chat' or 'normal'. If not empty, treat all incoming messages "
-"as if they were of this type"
-msgstr ""
-"Kann leer, 'chat' oder 'normal' sein. Wenn nicht leer, dann werden alle "
-"eingehenden Nachrichten so behandelt, als wären sie von diesem Typ."
-
-#: ../src/common/config.py:247
-msgid ""
-"If True, Gajim will scroll and select the contact who sent you the last "
-"message, if chat window is not already opened."
-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:248
-msgid ""
-"If True, Gajim will convert string between $$ and $$ to an image using dvips "
-"and convert before insterting it in chat window."
-msgstr ""
-"Falls 'true', wird Gajim eine Zeichenkette zwischen $$ und $$ mittels dvips "
-"vor dem Einfügen in ein Chat-Fenster in ein Bild umwandeln."
-
-#: ../src/common/config.py:249
-msgid "Time of inactivity needed before the change status window closes down."
-msgstr ""
-"Benötigte Inaktivitäts-Zeit bevor das 'Status ändern'-Fenster geschlossen "
-"wird."
-
-#: ../src/common/config.py:250
-msgid ""
-"Maximum number of lines that are printed in conversations. Oldest lines are "
-"cleared."
-msgstr ""
-"Maximale Anzahl Zeilen, die in einem Gespräch angezeigt werden. Die ältesten "
-"Zeilen werden gelöscht."
-
-#: ../src/common/config.py:259
-msgid ""
-"If True, notification windows from notification-daemon will be attached to "
-"systray icon."
-msgstr ""
-"Wenn wahr, werden Benachrichtigungsfenster vom Benachrichtigungsdienst an "
-"das Systray-Icon angehängt."
-
-#: ../src/common/config.py:260
-msgid "Choose interval between 2 checks of idleness."
-msgstr ""
-
-#: ../src/common/config.py:271
-msgid ""
-"Priority will change automatically according to your status. Priorities are "
-"defined in autopriority_* options."
-msgstr ""
-"Die Priorität wird automatisch dem Status entsprechend geändert. Prioritäten "
-"sind in den autopriority_* Optionen definiert."
-
-#: ../src/common/config.py:279
-#, fuzzy
-msgid ""
-"Status used to autoconnect as. Can be online, chat, away, xa, dnd, invisible."
-msgstr "Eins von: offline, online, chat, away, xa, dnd, invisible "
-
-#: ../src/common/config.py:284
-msgid ""
-"If disabled, don't sign presences with GPG key, even if GPG is configured."
-msgstr ""
-"Wenn deaktiviert, wird die Anwesenheit nicht mit einem GPG-Schlüssel "
-"signiert, wenn GPG konfiguriert ist."
-
-#: ../src/common/config.py:286
-msgid ""
-"Ordered list (space separated) of connection type to try. Can contain tls, "
-"ssl or plain"
-msgstr ""
-"Geordnete Liste (mit Leerzeichen getrennt) zu versuchender Verbindungstypen. "
-"Kann tls, ssl oder plain enthalten"
-
-#: ../src/common/config.py:287
-msgid ""
-"Show a warning dialog before sending password on an insecure connection."
-msgstr ""
-"Zeige einen Warndialog bevor das Passwort über eine unsichere Verbindung "
-"geschickt wird."
-
-#: ../src/common/config.py:289
-msgid "Space separated list of ssl errors to ignore."
-msgstr "Leerzeichen getrennte Liste von SSL-Fehler, die zu ignorieren sind."
-
-#: ../src/common/config.py:301
-msgid ""
-"How many seconds to wait for the answer of keepalive packet before we try to "
-"reconnect."
-msgstr ""
-
-#. yes, no, ask
-#: ../src/common/config.py:305
-msgid "Jabberd2 workaround"
-msgstr "Jabberd2 Workaround"
-
-#: ../src/common/config.py:309
-msgid ""
-"If checked, Gajim will use your IP and proxies defined in "
-"file_transfer_proxies option for file transfer."
-msgstr ""
-"Wenn aktiviert, wird Gajim Ihre IP und die in der file_transfer_proxies "
-"Option definierten Proxies für den Datentransfer verwenden."
-
-#: ../src/common/config.py:370
-msgid "Is OpenPGP enabled for this contact?"
-msgstr "Ist OpenPGP für diesen Kontakt aktiviert?"
-
-#: ../src/common/config.py:371 ../src/common/config.py:375
-msgid "Language for which we want to check misspelled words"
-msgstr "Sprache für, die nach falsch geschriebenen Wörtern gesucht werden soll"
-
-#: ../src/common/config.py:381
-msgid "all or space separated status"
-msgstr "alle oder Leerzeichen-getrennter Status"
-
-#: ../src/common/config.py:382
-msgid "'yes', 'no', or 'both'"
-msgstr "'ja', 'nein' oder 'beide'"
-
-#: ../src/common/config.py:383 ../src/common/config.py:385
-#: ../src/common/config.py:386 ../src/common/config.py:389
-#: ../src/common/config.py:390
-msgid "'yes', 'no' or ''"
-msgstr "'ja, 'nein' oder ''"
-
-#: ../src/common/config.py:396
-msgid "Sleeping"
-msgstr "Schlafen"
-
-#: ../src/common/config.py:397
-msgid "Back soon"
-msgstr "Bin gleich wieder da"
-
-#: ../src/common/config.py:397
-msgid "Back in some minutes."
-msgstr "Bin in ein paar Minuten zurück."
-
-#: ../src/common/config.py:398
-msgid "Eating"
-msgstr "Essen"
-
-#: ../src/common/config.py:398
-msgid "I'm eating, so leave me a message."
-msgstr "Ich esse gerade."
-
-#: ../src/common/config.py:399
-msgid "Movie"
-msgstr "Film"
-
-#: ../src/common/config.py:399
-msgid "I'm watching a movie."
-msgstr "Ich sehe mir einen Film an."
-
-#: ../src/common/config.py:400
-msgid "Working"
-msgstr "Arbeiten"
-
-#: ../src/common/config.py:400
-msgid "I'm working."
-msgstr "Ich arbeite."
-
-#: ../src/common/config.py:401
-msgid "Phone"
-msgstr "Telefon"
-
-#: ../src/common/config.py:401
-msgid "I'm on the phone."
-msgstr "Ich telefoniere."
-
-#: ../src/common/config.py:402
-msgid "Out"
-msgstr "Draußen"
-
-#: ../src/common/config.py:402
-msgid "I'm out enjoying life."
-msgstr "Ich bin draußen und genieße das Leben."
-
-#: ../src/common/config.py:406
-msgid "I'm available."
-msgstr "Ich bin angemeldet."
-
-#: ../src/common/config.py:407
-msgid "I'm free for chat."
-msgstr "Ich bin frei zum Chatten."
-
-#: ../src/common/config.py:409
-msgid "I'm not available."
-msgstr "Ich bin nicht verfügbar."
-
-#: ../src/common/config.py:410
-msgid "Do not disturb."
-msgstr "Bitte nicht stören."
-
-#: ../src/common/config.py:411 ../src/common/config.py:412
-msgid "Bye!"
-msgstr "Auf Wiedersehen!"
-
-#: ../src/common/config.py:422
-msgid ""
-"Sound to play when a group chat message contains one of the words in "
-"muc_highlight_words, or when a group chat message contains your nickname."
-msgstr ""
-"Abzuspielender Ton, falls eine Gruppenchat-Nachricht eines der Wörter aus "
-"der muc_highlights_works-Liste oder Ihren Spitznamen enthält."
-
-#: ../src/common/config.py:423
-msgid "Sound to play when any MUC message arrives."
-msgstr ""
-"Ton der beim empfangen einer Gruppenchat-Nachricht abgespielt werden soll"
-
-#: ../src/common/config.py:432 ../src/common/optparser.py:226
-msgid "green"
-msgstr "grün"
-
-#: ../src/common/config.py:436 ../src/common/optparser.py:212
-msgid "grocery"
-msgstr "gemüse"
-
-#: ../src/common/config.py:440
-msgid "human"
-msgstr "menschlich"
-
-#: ../src/common/config.py:444
-msgid "marine"
-msgstr "Marine"
-
-#: ../src/common/connection_handlers.py:68
-#: ../src/common/zeroconf/connection_handlers_zeroconf.py:48
-msgid "Unable to load idle module"
-msgstr "Konnte Idle-Modus nicht laden"
-
-#: ../src/common/connection_handlers.py:226
-#: ../src/common/zeroconf/connection_handlers_zeroconf.py:242
-msgid "Wrong host"
-msgstr "Falscher Host"
-
-#: ../src/common/connection_handlers.py:227
-msgid "Invalid local address? :-O"
-msgstr "Ungültige Lokale Adresse? :-O"
-
-#: ../src/common/connection_handlers.py:628
-#, python-format
-msgid "Registration information for transport %s has not arrived in time"
-msgstr ""
-"Registrierungsinformation für Transport %s sind nicht rechtzeitig angekommen"
-
-#: ../src/common/connection_handlers.py:1866
-#, python-format
-msgid "Nickname not allowed: %s"
-msgstr "Spitzname nicht erlaubt: %s"
-
-#. we are banned
-#. group chat does not exist
-#: ../src/common/connection_handlers.py:1936
-#: ../src/common/connection_handlers.py:1939
-#: ../src/common/connection_handlers.py:1942
-#: ../src/common/connection_handlers.py:1945
-#: ../src/common/connection_handlers.py:1949
-#: ../src/common/connection_handlers.py:1958
-msgid "Unable to join group chat"
-msgstr "Fehler beim Betreten des Gruppenchats"
-
-#: ../src/common/connection_handlers.py:1937
-#, python-format
-msgid "You are banned from group chat %s."
-msgstr "Sie sind von Gruppenchat %s gebannt."
-
-#: ../src/common/connection_handlers.py:1940
-#, python-format
-msgid "Group chat %s does not exist."
-msgstr "Dieser Gruppenchat existiert nicht."
-
-#: ../src/common/connection_handlers.py:1943
-msgid "Group chat creation is restricted."
-msgstr "Gruppenchaterstellung ist beschränkt."
-
-#: ../src/common/connection_handlers.py:1946
-#, python-format
-msgid "Your registered nickname must be used in group chat %s."
-msgstr "Sie müssen Ihren registrierten Spitznamen verwenden"
-
-#: ../src/common/connection_handlers.py:1950
-#, python-format
-msgid "You are not in the members list in groupchat %s."
-msgstr "Sie sind nicht in der Mitgliedliste"
-
-#: ../src/common/connection_handlers.py:1959
-#, python-format
-msgid ""
-"Your desired nickname in group chat %s is in use or registered by another "
-"occupant.\n"
-"Please specify another nickname below:"
-msgstr ""
-"Der gewünschte Benutzername wird bereits verwendet oder ist von einem "
-"anderen Benutzer registriert.\n"
-"Bitte geben Sie einen anderen Benutzernamen an:"
-
-#. Room has been destroyed. see
-#. http://www.xmpp.org/extensions/xep-0045.html#destroyroom
-#: ../src/common/connection_handlers.py:1990
-msgid "Room has been destroyed"
-msgstr "Raum wurde zerstört"
-
-#: ../src/common/connection_handlers.py:1997
-#, python-format
-msgid "You can join this room instead: %s"
-msgstr "Sie können stattdessen diesem Raum beitreten: %s"
-
-#: ../src/common/connection_handlers.py:2024
-msgid "I would like to add you to my roster."
-msgstr "Ich würde dich gerne zu meiner Liste hinzufügen."
-
-#. BE CAREFUL: no con.updateRosterItem() in a callback
-#: ../src/common/connection_handlers.py:2045
-#, python-format
-msgid "we are now subscribed to %s"
-msgstr "wir haben jetzt %s abonniert"
-
-#: ../src/common/connection_handlers.py:2047
-#, python-format
-msgid "unsubscribe request from %s"
-msgstr "Anfrage zur Kündigung des Abonnements von %s"
-
-#: ../src/common/connection_handlers.py:2049
-#, python-format
-msgid "we are now unsubscribed from %s"
-msgstr "%s hat das Abonnement beendet"
-
-#: ../src/common/connection_handlers.py:2190
-#, 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 ""
-"Jid %s ist nicht RFC-konform. Sie wird nicht zu ihrem Roster hinzugefügt. "
-"Verwenden Sie ein Roster-Managment-Tool wie <http://jru.jabberstudio.org/>, "
-"um ihn zu entfernen"
-
-#. We didn't set a passphrase
-#: ../src/common/connection_handlers.py:2215
-#: ../src/common/zeroconf/connection_zeroconf.py:172
-msgid "OpenPGP passphrase was not given"
-msgstr "Keine OpenPGP-Passphrase gewählt"
-
-#: ../src/common/connection.py:60
-msgid "Unable to get issuer certificate"
-msgstr "Kann Zertifikat des Ausstellers nicht holen"
-
-#: ../src/common/connection.py:61
-msgid "Unable to get certificate CRL"
-msgstr "Kann Zertifikats CRL nicht holen"
-
-#: ../src/common/connection.py:62
-msgid "Unable to decrypt certificate's signature"
-msgstr "Kann Zertifikatsunterschrift nicht entschlüsseln"
-
-#: ../src/common/connection.py:63
-msgid "Unable to decrypt CRL's signature"
-msgstr "Kann CRLs Unterschrift nicht entschlüsseln"
-
-#: ../src/common/connection.py:64
-msgid "Unable to decode issuer public key"
-msgstr "Kann öffentlichen Schlüssel nicht entschlüsseln"
-
-#: ../src/common/connection.py:65
-msgid "Certificate signature failure"
-msgstr "Zertifikat-Unterschrift-Fehler"
-
-#: ../src/common/connection.py:66
-msgid "CRL signature failure"
-msgstr "CRL-Unterschrift-Fehler"
-
-#: ../src/common/connection.py:67
-msgid "Certificate is not yet valid"
-msgstr "Zertifikat ist noch nicht gültig"
-
-#: ../src/common/connection.py:68
-msgid "Certificate has expired"
-msgstr "Zertifikat ist abgelaufen"
-
-#: ../src/common/connection.py:69
-msgid "CRL is not yet valid"
-msgstr "CRL ist nocht nicht gültig"
-
-#: ../src/common/connection.py:70
-msgid "CRL has expired"
-msgstr "CRL ist abgelaufen"
-
-#: ../src/common/connection.py:71
-msgid "Format error in certificate's notBefore field"
-msgstr "Formatfehler im notBefore Feld des Zertifikats"
-
-#: ../src/common/connection.py:72
-msgid "Format error in certificate's notAfter field"
-msgstr "Formatfehler im notAfter Feld des Zertifikats"
-
-#: ../src/common/connection.py:73
-msgid "Format error in CRL's lastUpdate field"
-msgstr "Formatfehler im lastUpdate Feld des CRLs"
-
-#: ../src/common/connection.py:74
-msgid "Format error in CRL's nextUpdate field"
-msgstr "Formatfehler in nextUpdate Feld des CRLs"
-
-#: ../src/common/connection.py:75
-msgid "Out of memory"
-msgstr "Zu wenig Speicher"
-
-#: ../src/common/connection.py:76
-msgid "Self signed certificate"
-msgstr "Selbstsigniertes Zertifikat"
-
-#: ../src/common/connection.py:77
-msgid "Self signed certificate in certificate chain"
-msgstr "Selbstsigniertes Zertifikat in der Zertifikatkette"
-
-#: ../src/common/connection.py:78
-msgid "Unable to get local issuer certificate"
-msgstr "Kann lokales Herausgeberzertifikat nicht holen"
-
-#: ../src/common/connection.py:79
-msgid "Unable to verify the first certificate"
-msgstr "Kann erstes Zertifikat nicht prüfen"
-
-#: ../src/common/connection.py:80
-msgid "Certificate chain too long"
-msgstr "Zertifikatkette zu lang"
-
-#: ../src/common/connection.py:81
-msgid "Certificate revoked"
-msgstr "Zertifikat widerrufen"
-
-#: ../src/common/connection.py:82
-msgid "Invalid CA certificate"
-msgstr "Ungültiges CA Zertifikat"
-
-#: ../src/common/connection.py:83
-msgid "Path length constraint exceeded"
-msgstr "Pfadlängenbeschränkung überschritten"
-
-#: ../src/common/connection.py:84
-msgid "Unsupported certificate purpose"
-msgstr "Nicht unterstütztes Zertifikatsverwendungszweck"
-
-#: ../src/common/connection.py:85
-msgid "Certificate not trusted"
-msgstr "Zertifikat nicht vertrauenswürdig"
-
-#: ../src/common/connection.py:86
-msgid "Certificate rejected"
-msgstr "Zertifikat abgelehnt"
-
-#: ../src/common/connection.py:87
-msgid "Subject issuer mismatch"
-msgstr ""
-
-#: ../src/common/connection.py:88
-msgid "Authority and subject key identifier mismatch"
-msgstr ""
-
-#: ../src/common/connection.py:89
-msgid "Authority and issuer serial number mismatch"
-msgstr ""
-
-#: ../src/common/connection.py:90
-msgid "Key usage does not include certificate signing"
-msgstr ""
-
-#: ../src/common/connection.py:91
-msgid "Application verification failure"
-msgstr ""
-
-#: ../src/common/connection.py:256
-#: ../src/common/zeroconf/connection_zeroconf.py:214
-#, python-format
-msgid "Connection with account \"%s\" has been lost"
-msgstr "Verbindung mit Konto \"%s\" abgebrochen"
-
-#: ../src/common/connection.py:257
-msgid "Reconnect manually."
-msgstr "Manuelle Neuverbindung."
-
-#: ../src/common/connection.py:268
-#, python-format
-msgid "Server %s answered wrongly to register request: %s"
-msgstr "Server %s beantwortete Registrierungsanfrage nicht korrekt: %s"
-
-#: ../src/common/connection.py:302
-#, python-format
-msgid "Server %s provided a different registration form"
-msgstr "Server %s bot ein anderes Registrierungsformular"
-
-#: ../src/common/connection.py:318
-#, python-format
-msgid "Unknown SSL error: %d"
-msgstr "Unbekannter SSL-Fehler: %d"
-
-#. wrong answer
-#: ../src/common/connection.py:333
-msgid "Invalid answer"
-msgstr "Ungültige Antwort"
-
-#: ../src/common/connection.py:334
-#, python-format
-msgid "Transport %s answered wrongly to register request: %s"
-msgstr ""
-"Transport %s beantwortete unsere Registrierungsanfrage nicht korrekt: %s"
-
-#: ../src/common/connection.py:515
-msgid "Connection to proxy failed"
-msgstr "Verbindung mit Proxy fehlgeschlagen"
-
-#: ../src/common/connection.py:592 ../src/common/connection.py:672
-#: ../src/common/connection.py:1279
-#: ../src/common/zeroconf/connection_zeroconf.py:248
-#, python-format
-msgid "Could not connect to \"%s\""
-msgstr "Konnte nicht mit %s verbinden"
-
-#: ../src/common/connection.py:629
-#, python-format
-msgid "The authenticity of the %s certificate could be invalid."
-msgstr "Die Authentizität des %s Zertifikats könnte ungültig sein."
-
-#: ../src/common/connection.py:632
-#, python-format
-msgid ""
-"\n"
-"SSL Error: <b>%s</b>"
-msgstr ""
-"\n"
-"SSL-Fehler: <b>%s</b>"
-
-#: ../src/common/connection.py:634
-#, python-format
-msgid ""
-"\n"
-"Unknown SSL error: %d"
-msgstr ""
-"\n"
-"Unbekannter SSL-Fehler: %d"
-
-#: ../src/common/connection.py:673
-msgid "Check your connection or try again later"
-msgstr "Überprüfen Sie die Verbindung oder versuchen Sie es später noch einmal"
-
-#: ../src/common/connection.py:698
-#, python-format
-msgid "Authentication failed with \"%s\""
-msgstr "Authentifizierung mit \"%s\" fehlgeschlagen"
-
-#: ../src/common/connection.py:700
-msgid "Please check your login and password for correctness."
-msgstr "Bitte überprüfen Sie ihren Benutzernamen und Passwort."
-
-#: ../src/common/connection.py:761
-msgid "Error while removing privacy list"
-msgstr "Fehler beim Entfernen der Privatliste"
-
-#: ../src/common/connection.py:762
-#, python-format
-msgid ""
-"Privacy list %s has not been removed. It is maybe active in one of your "
-"connected resources. Deactivate it and try again."
-msgstr ""
-"Privatliste %s wurde nicht entfernt. Möglicherweise ist die Privatliste noch "
-"in einer Ihrer Verbindungen aktiv. Deaktivieren Sie diese und versuchen Sie "
-"es erneut."
-
-#: ../src/common/connection.py:1058
-#: ../src/common/zeroconf/connection_zeroconf.py:381
-msgid "Neither the remote presence is signed, nor a key was assigned."
-msgstr ""
-"Weder die entfernte Präsenz ist signiert, noch wurde ein Schlüssel "
-"zugewiesen."
-
-#: ../src/common/connection.py:1060
-#: ../src/common/zeroconf/connection_zeroconf.py:383
-#, python-format
-msgid "The contact's key (%s) does not match the key assigned in Gajim."
-msgstr ""
-"Der Kontaktschlüssel (%s) stimmt nicht mit dem in Gajim zugewiesenen "
-"Schlüssel überein."
-
-#. we're not english
-#. one in locale and one en
-#: ../src/common/connection.py:1069
-#, fuzzy
-msgid "[This message is *encrypted* (See :XEP:`27`]"
-msgstr "[Diese Nachricht ist *verschlüsselt* (Siehe: JEP:`27`)]"
-
-#: ../src/common/connection.py:1146
-#: ../src/common/zeroconf/connection_zeroconf.py:445
-#, python-format
-msgid ""
-"Subject: %s\n"
-"%s"
-msgstr ""
-"Thema: %s\n"
-"%s"
-
-#: ../src/common/connection.py:1312
-msgid "Not fetched because of invisible status"
-msgstr "Nicht abgeholt aufgrund eines Unsichtbar-Status"
-
-#: ../src/common/contacts.py:307
-msgid "Not in roster"
-msgstr "Nicht in der Kontaktliste"
-
-#. only say that to non Windows users
-#: ../src/common/dbus_support.py:41
-msgid "D-Bus python bindings are missing in this computer"
-msgstr "Auf diesem Computer fehlen die Python Bibliotheken für D-Bus"
-
-#: ../src/common/dbus_support.py:42
-msgid "D-Bus capabilities of Gajim cannot be used"
-msgstr "Gajims D-BUS-Möglichkeiten können nicht genutzt werden"
-
-#: ../src/common/exceptions.py:27
-msgid "pysqlite2 (aka python-pysqlite2) dependency is missing. Exiting..."
-msgstr ""
-"pysqlite2 (auch python-pysqlite2 genannt) Abhängigkeit fehlt. Beende ..."
-
-#: ../src/common/exceptions.py:44
-msgid "Database cannot be read."
-msgstr "Datenbank kann nicht gelesen werden."
-
-#: ../src/common/exceptions.py:52
-msgid "Service not available: Gajim is not running, or remote_control is False"
-msgstr ""
-"Dienst nicht verfügbar: Gajim ist nicht aktiv oder remote_control ist "
-"deaktiviert"
-
-#: ../src/common/exceptions.py:60
-msgid "D-Bus is not present on this machine or python module is missing"
-msgstr "D-Bus auf diesem Computer nicht vorhanden oder das Python-Modul fehlt"
-
-#: ../src/common/exceptions.py:68
-msgid ""
-"Session bus is not available.\n"
-"Try reading http://trac.gajim.org/wiki/GajimDBus"
-msgstr ""
-"Sitzungsbus ist nicht verfügbar.\"Bitte lesen Sie http://trac.gajim.org/wiki/"
-"GajimDBus (Englisch)"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "one"
-msgstr "Eins"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "two"
-msgstr "Zwei"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "three"
-msgstr "Drei"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "four"
-msgstr "Vier"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "five"
-msgstr "Fünf"
-
-#: ../src/common/fuzzyclock.py:47
-msgid "six"
-msgstr "Sechs"
-
-#: ../src/common/fuzzyclock.py:48
-msgid "seven"
-msgstr "Sieben"
-
-#: ../src/common/fuzzyclock.py:48
-msgid "eight"
-msgstr "Acht"
-
-#: ../src/common/fuzzyclock.py:48
-msgid "nine"
-msgstr "Neun"
-
-#: ../src/common/fuzzyclock.py:48
-msgid "ten"
-msgstr "Zehn"
-
-#: ../src/common/fuzzyclock.py:48
-msgid "eleven"
-msgstr "Elf"
-
-#: ../src/common/fuzzyclock.py:49
-msgid "twelve"
-msgstr "Zwölf"
-
-#. Strings to use for the output. %0 will be replaced with the preceding hour (e.g. "x PAST %0"), %1 with the coming hour (e.g. "x TO %1). '''
-#. A "singular-form". It is used when talking about hour 0
-#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60
-msgid "$0 o'clock"
-msgstr "%0 Uhr"
-
-#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60
-msgid "five past $0"
-msgstr "fünf nach %0"
-
-#: ../src/common/fuzzyclock.py:53 ../src/common/fuzzyclock.py:61
-msgid "ten past $0"
-msgstr "zehn nach %0"
-
-#: ../src/common/fuzzyclock.py:53 ../src/common/fuzzyclock.py:61
-msgid "quarter past $0"
-msgstr "viertel nach %0"
-
-#: ../src/common/fuzzyclock.py:54 ../src/common/fuzzyclock.py:62
-msgid "twenty past $0"
-msgstr "zwanzig nach %0"
-
-#: ../src/common/fuzzyclock.py:54 ../src/common/fuzzyclock.py:62
-msgid "twenty five past $0"
-msgstr "fünfundzwanzig nach %0"
-
-#: ../src/common/fuzzyclock.py:55 ../src/common/fuzzyclock.py:63
-msgid "half past $0"
-msgstr "dreißig nach %0"
-
-#: ../src/common/fuzzyclock.py:55 ../src/common/fuzzyclock.py:63
-msgid "twenty five to $1"
-msgstr "fünfundzwanzig vor %1"
-
-#: ../src/common/fuzzyclock.py:56 ../src/common/fuzzyclock.py:64
-msgid "twenty to $1"
-msgstr "zwanzig vor %1"
-
-#: ../src/common/fuzzyclock.py:56 ../src/common/fuzzyclock.py:64
-msgid "quarter to $1"
-msgstr "viertel vor %1"
-
-#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:65
-msgid "ten to $1"
-msgstr "zehn vor %1"
-
-#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:65
-msgid "five to $1"
-msgstr "fünf vor %1"
-
-#: ../src/common/fuzzyclock.py:57 ../src/common/fuzzyclock.py:66
-msgid "$1 o'clock"
-msgstr "%1 Uhr"
-
-#: ../src/common/fuzzyclock.py:69
-msgid "Night"
-msgstr "Nacht"
-
-#: ../src/common/fuzzyclock.py:69
-msgid "Early morning"
-msgstr "Früh Morgens"
-
-#: ../src/common/fuzzyclock.py:69
-msgid "Morning"
-msgstr "Morgen"
-
-#: ../src/common/fuzzyclock.py:69
-msgid "Almost noon"
-msgstr "Fast Mittag"
-
-#: ../src/common/fuzzyclock.py:70
-msgid "Noon"
-msgstr "Mittag"
-
-#: ../src/common/fuzzyclock.py:70
-msgid "Afternoon"
-msgstr "Nachmittag"
-
-#: ../src/common/fuzzyclock.py:70
-msgid "Evening"
-msgstr "Abend"
-
-#: ../src/common/fuzzyclock.py:70
-msgid "Late evening"
-msgstr "Später Abend"
-
-#: ../src/common/fuzzyclock.py:72
-msgid "Start of week"
-msgstr "Anfang der Woche"
-
-#: ../src/common/fuzzyclock.py:72
-msgid "Middle of week"
-msgstr "Mitte der Woche"
-
-#: ../src/common/fuzzyclock.py:72
-msgid "End of week"
-msgstr "Ende der Woche"
-
-#: ../src/common/fuzzyclock.py:73
-msgid "Weekend!"
-msgstr "Wochenende!"
-
-#: ../src/common/helpers.py:137
-msgid "Invalid character in username."
-msgstr "Ungültiges Zeichen im Benutzernamen"
-
-#: ../src/common/helpers.py:142
-msgid "Server address required."
-msgstr "Server-Adresse wird benötigt."
-
-#: ../src/common/helpers.py:147
-msgid "Invalid character in hostname."
-msgstr "Ungültiges Zeichen in Hostname."
-
-#: ../src/common/helpers.py:153
-msgid "Invalid character in resource."
-msgstr "Ungültiges Zeichen in Resource."
-
-#. GiB means gibibyte
-#: ../src/common/helpers.py:193
-#, python-format
-msgid "%s GiB"
-msgstr "%s GiB"
-
-#. GB means gigabyte
-#: ../src/common/helpers.py:196
-#, python-format
-msgid "%s GB"
-msgstr "%s GB"
-
-#. MiB means mibibyte
-#: ../src/common/helpers.py:200
-#, python-format
-msgid "%s MiB"
-msgstr "%s MiB"
-
-#. MB means megabyte
-#: ../src/common/helpers.py:203
-#, python-format
-msgid "%s MB"
-msgstr "%s MB"
-
-#. KiB means kibibyte
-#: ../src/common/helpers.py:207
-#, python-format
-msgid "%s KiB"
-msgstr "%s KiB"
-
-#. KB means kilo bytes
-#: ../src/common/helpers.py:210
-#, python-format
-msgid "%s KB"
-msgstr "%s kB"
-
-#. B means bytes
-#: ../src/common/helpers.py:213
-#, python-format
-msgid "%s B"
-msgstr "%s B"
-
-#: ../src/common/helpers.py:244
-msgid "_Busy"
-msgstr "_Beschäftigt"
-
-#: ../src/common/helpers.py:246
-msgid "Busy"
-msgstr "Beschäftigt"
-
-#: ../src/common/helpers.py:249
-msgid "_Not Available"
-msgstr "_Nicht verfügbar"
-
-#: ../src/common/helpers.py:254
-msgid "_Free for Chat"
-msgstr "_Frei zum Chatten"
-
-#: ../src/common/helpers.py:256
-msgid "Free for Chat"
-msgstr "Frei zum Chatten"
-
-#: ../src/common/helpers.py:259
-msgid "_Available"
-msgstr "_Angemeldet"
-
-#: ../src/common/helpers.py:263
-msgid "Connecting"
-msgstr "Verbinde"
-
-#: ../src/common/helpers.py:266
-msgid "A_way"
-msgstr "Ab_wesend"
-
-#: ../src/common/helpers.py:271
-msgid "_Offline"
-msgstr "A_bgemeldet"
-
-#: ../src/common/helpers.py:273
-msgid "Offline"
-msgstr "Abgemeldet"
-
-#: ../src/common/helpers.py:276
-msgid "_Invisible"
-msgstr "_Unsichtbar"
-
-#: ../src/common/helpers.py:282
-msgid "?contact has status:Unknown"
-msgstr "Unbekannt"
-
-#: ../src/common/helpers.py:284
-msgid "?contact has status:Has errors"
-msgstr "Hat Fehler"
-
-#: ../src/common/helpers.py:289
-msgid "?Subscription we already have:None"
-msgstr "Keine"
-
-#: ../src/common/helpers.py:291
-msgid "To"
-msgstr "An"
-
-#: ../src/common/helpers.py:295
-msgid "Both"
-msgstr "Beide"
-
-#: ../src/common/helpers.py:303
-msgid "?Ask (for Subscription):None"
-msgstr "Keine"
-
-#: ../src/common/helpers.py:305
-msgid "Subscribe"
-msgstr "Abonnieren"
-
-#: ../src/common/helpers.py:314
-msgid "?Group Chat Contact Role:None"
-msgstr "Keine"
-
-#: ../src/common/helpers.py:317
-msgid "Moderators"
-msgstr "Moderatoren"
-
-#: ../src/common/helpers.py:319
-msgid "Moderator"
-msgstr "Moderator"
-
-#: ../src/common/helpers.py:322
-msgid "Participants"
-msgstr "Teilnehmer"
-
-#: ../src/common/helpers.py:324
-msgid "Participant"
-msgstr "Teilnehmer"
-
-#: ../src/common/helpers.py:327
-msgid "Visitors"
-msgstr "Besucher"
-
-#: ../src/common/helpers.py:329
-msgid "Visitor"
-msgstr "Besucher"
-
-#: ../src/common/helpers.py:335
-msgid "?Group Chat Contact Affiliation:None"
-msgstr "?Gruppenchat Kontaktverbindung:Keine"
-
-#: ../src/common/helpers.py:337
-msgid "Owner"
-msgstr "Besitzer"
-
-#: ../src/common/helpers.py:339
-msgid "Administrator"
-msgstr "Administrator"
-
-#: ../src/common/helpers.py:341
-msgid "Member"
-msgstr "Mitglied"
-
-#: ../src/common/helpers.py:348
-msgid "afraid"
-msgstr "Besorgt"
-
-#: ../src/common/helpers.py:348
-msgid "amazed"
-msgstr "Erstaunt"
-
-#: ../src/common/helpers.py:348
-msgid "angry"
-msgstr "Aufgebracht"
-
-#: ../src/common/helpers.py:349
-msgid "annoyed"
-msgstr "Verärgert"
-
-#: ../src/common/helpers.py:349
-msgid "anxious"
-msgstr "Bemüht"
-
-#: ../src/common/helpers.py:349
-msgid "aroused"
-msgstr "Erregt"
-
-#: ../src/common/helpers.py:350
-msgid "ashamed"
-msgstr "Beschämt"
-
-#: ../src/common/helpers.py:350
-msgid "bored"
-msgstr "Gelangweilt"
-
-#: ../src/common/helpers.py:350
-msgid "brave"
-msgstr "Tapfer"
-
-#: ../src/common/helpers.py:351
-msgid "calm"
-msgstr "Gelassen"
-
-#: ../src/common/helpers.py:351
-msgid "cold"
-msgstr "Kalt"
-
-#: ../src/common/helpers.py:351
-msgid "confused"
-msgstr "Verwirrt"
-
-#: ../src/common/helpers.py:352
-msgid "contented"
-msgstr "Zufrieden"
-
-#: ../src/common/helpers.py:352
-msgid "cranky"
-msgstr "Launisch"
-
-#: ../src/common/helpers.py:352
-msgid "curious"
-msgstr "Neugierig"
-
-#: ../src/common/helpers.py:353
-msgid "depressed"
-msgstr "Deprimiert"
-
-#: ../src/common/helpers.py:353
-msgid "disappointed"
-msgstr "Enttäuscht"
-
-#: ../src/common/helpers.py:354
-msgid "disgusted"
-msgstr "Empört"
-
-#: ../src/common/helpers.py:354
-msgid "distracted"
-msgstr "Abgelenkt"
-
-#: ../src/common/helpers.py:355
-msgid "embarrassed"
-msgstr "Verlegen"
-
-#: ../src/common/helpers.py:355
-msgid "excited"
-msgstr "Aufgeregt"
-
-#: ../src/common/helpers.py:356
-msgid "flirtatious"
-msgstr "Kokett"
-
-#: ../src/common/helpers.py:356
-msgid "frustrated"
-msgstr "Frustriert"
+#~ msgid "Click to see past conversation in this room"
+#~ msgstr "Klicken, um die früheren Unterhaltungen in diesem Raum zu sehen"
-#: ../src/common/helpers.py:357
-msgid "grumpy"
-msgstr "Mürrisch"
-
-#: ../src/common/helpers.py:357
-msgid "guilty"
-msgstr "Schuldig"
-
-#: ../src/common/helpers.py:357
-msgid "happy"
-msgstr "Fröhlich"
-
-#: ../src/common/helpers.py:358
-msgid "hot"
-msgstr "Heiß"
+#~ msgid "<b>Publish and Subscribe</b>"
+#~ msgstr "<b>Veröffentlichen und Abonnieren</b>"
-#: ../src/common/helpers.py:358
-msgid "humbled"
-msgstr "Demütig"
+#~ msgid "Allow others to see your:"
+#~ msgstr "Erlaube anderen zu sehen:"
-#: ../src/common/helpers.py:358
-msgid "humiliated"
-msgstr "Beschämt"
+#~ msgid "Receive your contact's:"
+#~ msgstr "Empfang von Kontakten:"
-#: ../src/common/helpers.py:359
-msgid "hungry"
-msgstr "Hungrig"
+#~ msgid "Tune"
+#~ msgstr "Musiktitel:"
-#: ../src/common/helpers.py:359
-msgid "hurt"
-msgstr "Verletzt"
+#~ msgid "The following message was NOT encrypted"
+#~ msgstr "[Die folgende Nachricht wurde nicht verschlüsselt]"
-#: ../src/common/helpers.py:359
-msgid "impressed"
-msgstr "Beeindruckt"
-
-#: ../src/common/helpers.py:360
-#, fuzzy
-msgid "in awe"
-msgstr "Ehrfürchtig"
-
-#: ../src/common/helpers.py:360
-#, fuzzy
-msgid "in love"
-msgstr "Verliebt"
-
-#: ../src/common/helpers.py:360
-msgid "indignant"
-msgstr "Entrüstet"
-
-#: ../src/common/helpers.py:361
-msgid "interested"
-msgstr "Interessiert"
-
-#: ../src/common/helpers.py:361
-msgid "intoxicated"
-msgstr "Betrunken"
-
-#: ../src/common/helpers.py:362
-msgid "invincible"
-msgstr "unsichtbar"
-
-#: ../src/common/helpers.py:362
-msgid "jealous"
-msgstr "Neidisch"
-
-#: ../src/common/helpers.py:362
-msgid "lonely"
-msgstr "Einsam"
-
-#: ../src/common/helpers.py:363
-msgid "mean"
-msgstr "Fies"
-
-#: ../src/common/helpers.py:363
-msgid "moody"
-msgstr "Launisch"
-
-#: ../src/common/helpers.py:363
-msgid "nervous"
-msgstr "Nervös"
-
-#: ../src/common/helpers.py:364
-msgid "neutral"
-msgstr "Neutral"
-
-#: ../src/common/helpers.py:364
-msgid "offended"
-msgstr "Beleidigt"
-
-#: ../src/common/helpers.py:364
-msgid "playful"
-msgstr "Verspielt"
-
-#: ../src/common/helpers.py:365
-msgid "proud"
-msgstr "Stolz"
-
-#: ../src/common/helpers.py:365
-msgid "relieved"
-msgstr "Erleichtert"
-
-#: ../src/common/helpers.py:365
-msgid "remorseful"
-msgstr "Reumütig"
-
-#: ../src/common/helpers.py:366
-msgid "restless"
-msgstr "Ruhelos"
-
-#: ../src/common/helpers.py:366
-msgid "sad"
-msgstr "Traurig"
-
-#: ../src/common/helpers.py:366
-msgid "sarcastic"
-msgstr "Sarkastisch"
-
-#: ../src/common/helpers.py:367
-msgid "serious"
-msgstr "Ernst"
-
-#: ../src/common/helpers.py:367
-msgid "shocked"
-msgstr "Schockiert"
-
-#: ../src/common/helpers.py:367
-msgid "shy"
-msgstr "Schüchtern"
-
-#: ../src/common/helpers.py:368
-msgid "sick"
-msgstr "Krank"
-
-#: ../src/common/helpers.py:368
-msgid "sleepy"
-msgstr "Schläfrig"
-
-#: ../src/common/helpers.py:368
-msgid "stressed"
-msgstr "Gestreßt"
-
-#: ../src/common/helpers.py:369
-msgid "surprised"
-msgstr "Ãœberrascht"
-
-#: ../src/common/helpers.py:369
-msgid "thirsty"
-msgstr "Durstig"
-
-#: ../src/common/helpers.py:369
-msgid "worried"
-msgstr "Besorgt"
-
-#. Activities
-#: ../src/common/helpers.py:380
-msgid "working"
-msgstr "Arbeiten"
-
-#: ../src/common/helpers.py:380
-msgid "eating"
-msgstr "Essen"
-
-#: ../src/common/helpers.py:381
-msgid "excercising"
-msgstr ""
-
-#: ../src/common/helpers.py:381
-msgid "relaxing"
-msgstr "Entspannen"
-
-#: ../src/common/helpers.py:381
-msgid "talking"
-msgstr "Im Gespräch"
-
-#: ../src/common/helpers.py:382
-#, fuzzy
-msgid "doing chores"
-msgstr "Mache den Haushalt"
-
-#: ../src/common/helpers.py:382
-msgid "inactive"
-msgstr "Inaktiv"
-
-#: ../src/common/helpers.py:383
-msgid "traveling"
-msgstr "Auf Reisen"
-
-#: ../src/common/helpers.py:383
-#, fuzzy
-msgid "having an appointment"
-msgstr "Habe einen Termin"
-
-#: ../src/common/helpers.py:384
-msgid "grooming"
-msgstr "Putzen"
-
-#: ../src/common/helpers.py:384
-msgid "drinking"
-msgstr "In der Kneipe"
-
-#. Subactivites
-#: ../src/common/helpers.py:386
-#, fuzzy
-msgid "on the phone"
-msgstr "Am Telefon"
-
-#: ../src/common/helpers.py:386
-msgid "gardening"
-msgstr "Gartenarbeit"
-
-#: ../src/common/helpers.py:387
-msgid "hiking"
-msgstr "Wandern"
-
-#: ../src/common/helpers.py:387
-#, fuzzy
-msgid "on vacation"
-msgstr "Im Urlaub"
-
-#: ../src/common/helpers.py:387
-msgid "coding"
-msgstr "Programmieren"
-
-#: ../src/common/helpers.py:388
-msgid "walking"
-msgstr "Spazieren gehen"
-
-#: ../src/common/helpers.py:388
-msgid "rehearsing"
-msgstr "Studieren"
-
-#: ../src/common/helpers.py:388
-msgid "sleeping"
-msgstr "Schlafen"
-
-#: ../src/common/helpers.py:389
-#, fuzzy
-msgid "brushing teeth"
-msgstr "Zähne putzen"
-
-#: ../src/common/helpers.py:389
-#, fuzzy
-msgid "playing sports"
-msgstr "Beim Sport"
-
-#: ../src/common/helpers.py:390
-msgid "skiing"
-msgstr "Ski fahren"
-
-#: ../src/common/helpers.py:390
-#, fuzzy
-msgid "having breakfast"
-msgstr "Frühstücken"
-
-#: ../src/common/helpers.py:391
-#, fuzzy
-msgid "watching TV"
-msgstr "Fernsehen"
-
-#: ../src/common/helpers.py:391
-#, fuzzy
-msgid "doing the dishes"
-msgstr "Geschirr spülen"
-
-#: ../src/common/helpers.py:392
-#, fuzzy
-msgid "at the spa"
-msgstr "Im Bad"
-
-#: ../src/common/helpers.py:392
-msgid "cycling"
-msgstr "Rad fahren"
-
-#: ../src/common/helpers.py:393
-#, fuzzy
-msgid "hanging out"
-msgstr "Rumhängen"
-
-#: ../src/common/helpers.py:393
-msgid "driving"
-msgstr "Fahren"
-
-#: ../src/common/helpers.py:393
-msgid "commuting"
-msgstr "Pendeln"
-
-#: ../src/common/helpers.py:394
-msgid "cooking"
-msgstr "Kochen"
-
-#: ../src/common/helpers.py:394
-#, fuzzy
-msgid "walking the dog"
-msgstr "Hund ausführen"
-
-#: ../src/common/helpers.py:395
-msgid "writing"
-msgstr "Schreiben"
-
-#: ../src/common/helpers.py:395
-#, fuzzy
-msgid "on a trip"
-msgstr "Auf Reisen"
-
-#: ../src/common/helpers.py:395
-#, fuzzy
-msgid "day off"
-msgstr "Ruhetag"
-
-#: ../src/common/helpers.py:396
-#, fuzzy
-msgid "having tea"
-msgstr "Tee"
-
-#: ../src/common/helpers.py:396
-#, fuzzy
-msgid "on a bus"
-msgstr "Im Bus"
-
-#: ../src/common/helpers.py:397
-#, fuzzy
-msgid "having a beer"
-msgstr "Bier trinken"
-
-#: ../src/common/helpers.py:397
-msgid "reading"
-msgstr "Lesen"
-
-#: ../src/common/helpers.py:398
-#, fuzzy
-msgid "buying groceries"
-msgstr "Einkaufen"
-
-#: ../src/common/helpers.py:398
-msgid "shaving"
-msgstr "Beim Rasieren"
-
-#: ../src/common/helpers.py:399
-#, fuzzy
-msgid "getting a haircut"
-msgstr "Beim Frisör"
-
-#: ../src/common/helpers.py:399
-msgid "gaming"
-msgstr "Spielen"
-
-#: ../src/common/helpers.py:400
-#, fuzzy
-msgid "having dinner"
-msgstr "Abendessen"
-
-#: ../src/common/helpers.py:400
-#, fuzzy
-msgid "doing maintenance"
-msgstr "Wartungsarbeiten"
-
-#: ../src/common/helpers.py:401
-#, fuzzy
-msgid "doing the laundry"
-msgstr "Wäsche waschen"
-
-#: ../src/common/helpers.py:401
-#, fuzzy
-msgid "on video phone"
-msgstr "Ich telefoniere."
-
-#: ../src/common/helpers.py:402
-#, fuzzy
-msgid "scheduled holiday"
-msgstr "Regulärer Feiertag"
-
-#: ../src/common/helpers.py:402
-#, fuzzy
-msgid "going out"
-msgstr "Ausgehen"
-
-#: ../src/common/helpers.py:403
-msgid "partying"
-msgstr "Feiern"
-
-#: ../src/common/helpers.py:403
-#, fuzzy
-msgid "having a snack"
-msgstr "Beim Imbiss"
-
-#: ../src/common/helpers.py:404
-#, fuzzy
-msgid "having lunch"
-msgstr "Mittagessen"
-
-#: ../src/common/helpers.py:404
-#, fuzzy
-msgid "working out"
-msgstr "Berechnen"
-
-#: ../src/common/helpers.py:405
-msgid "cleaning"
-msgstr "Putzen"
-
-#: ../src/common/helpers.py:405
-#, fuzzy
-msgid "watching a movie"
-msgstr "Film anschauen"
-
-#: ../src/common/helpers.py:406
-msgid "sunbathing"
-msgstr "Sonnenbaden"
-
-#: ../src/common/helpers.py:406
-msgid "socializing"
-msgstr "Kontakte knüpfen"
-
-#: ../src/common/helpers.py:407
-#, fuzzy
-msgid "running an errand"
-msgstr "Besorgungen machen"
-
-#: ../src/common/helpers.py:407
-#, fuzzy
-msgid "taking a bath"
-msgstr "Ein Bad nehmen"
-
-#: ../src/common/helpers.py:408
-#, fuzzy
-msgid "in real life"
-msgstr "Im wahren Leben"
-
-#: ../src/common/helpers.py:408
-#, fuzzy
-msgid "on a plane"
-msgstr "Im Flugzeug"
-
-#: ../src/common/helpers.py:409
-msgid "shopping"
-msgstr "Beim Einkaufen"
-
-#: ../src/common/helpers.py:409
-#, fuzzy
-msgid "on a train"
-msgstr "Im Zug"
-
-#: ../src/common/helpers.py:409
-msgid "running"
-msgstr "Laufen"
-
-#: ../src/common/helpers.py:410
-#, fuzzy
-msgid "taking a shower"
-msgstr "Duschen"
-
-#: ../src/common/helpers.py:410
-msgid "jogging"
-msgstr "Beim Joggen"
-
-#: ../src/common/helpers.py:411
-#, fuzzy
-msgid "in a meeting"
-msgstr "In einer Besprechung"
-
-#: ../src/common/helpers.py:411
-#, fuzzy
-msgid "in a car"
-msgstr "Im Auto"
-
-#: ../src/common/helpers.py:412
-msgid "studying"
-msgstr "Lerne"
-
-#: ../src/common/helpers.py:412
-msgid "swimming"
-msgstr "Schwimmen"
-
-#: ../src/common/helpers.py:413
-#, fuzzy
-msgid "having coffee"
-msgstr "Kaffee trinken"
-
-#: ../src/common/helpers.py:452
-msgid "is paying attention to the conversation"
-msgstr "beobachtet diese Unterhaltung"
-
-#: ../src/common/helpers.py:454
-msgid "is doing something else"
-msgstr "tut etwas anderes"
-
-#: ../src/common/helpers.py:456
-msgid "is composing a message..."
-msgstr "schreibt im Moment ..."
-
-#. paused means he or she was composing but has stopped for a while
-#: ../src/common/helpers.py:459
-msgid "paused composing a message"
-msgstr "macht gerade eine Schreibpause"
-
-#: ../src/common/helpers.py:461
-msgid "has closed the chat window or tab"
-msgstr "hat das Chatfenster oder den Tab geschlossen"
-
-#: ../src/common/helpers.py:1032 ../src/common/helpers.py:1039
-#, python-format
-msgid "%d message pending"
-msgid_plural "%d messages pending"
-msgstr[0] "%d Nachricht schwebend"
-msgstr[1] "%d Nachrichten schwebend"
-
-#: ../src/common/helpers.py:1045
-#, python-format
-msgid " from room %s"
-msgstr "Von Gruppenchat %s"
-
-#: ../src/common/helpers.py:1048 ../src/common/helpers.py:1067
-#, python-format
-msgid " from user %s"
-msgstr "Von Benutzer %s"
-
-#: ../src/common/helpers.py:1050
-#, python-format
-msgid " from %s"
-msgstr "Von %s"
-
-#: ../src/common/helpers.py:1057 ../src/common/helpers.py:1064
-#, python-format
-msgid "%d event pending"
-msgid_plural "%d events pending"
-msgstr[0] "%d Ereignis anstehend"
-msgstr[1] "%d Ereignisse anstehend"
-
-#: ../src/common/helpers.py:1097
-#, python-format
-msgid "Gajim - %s"
-msgstr "Gajim - %s"
-
-#. we talk about a file
-#: ../src/common/optparser.py:65
-#, python-format
-msgid "error: cannot open %s for reading"
-msgstr "Fehler: %s kann nicht zum Lesen geöffnet werden"
-
-#: ../src/common/optparser.py:221 ../src/common/optparser.py:222
-msgid "cyan"
-msgstr "cyan"
-
-#: ../src/common/optparser.py:338
-msgid "migrating logs database to indices"
-msgstr "migriere Logdatenbank zu Indices"
-
-#: ../src/common/passwords.py:86
-#, python-format
-msgid "Gajim account %s"
-msgstr "Gajim-Konto %s"
-
-#: ../src/common/zeroconf/client_zeroconf.py:408
-msgid ""
-"Connection to host could not be established: Timeout while sending data."
-msgstr ""
-"Verbindung zum Host konnte nicht hergestellt werden: Zeitüberschreitung beim "
-"Senden von Daten."
-
-#: ../src/common/zeroconf/connection_handlers_zeroconf.py:242
-#, 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 %s, den Sie für die erweiterte Option ft_override_host_to_send "
-"angeben haben ist ungültig und wird ignoriert."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:215
-msgid "To continue sending and receiving messages, you will need to reconnect."
-msgstr ""
-"Um weiterhin Nachrichten Senden und Empfangen zu können, müssen Sie sich "
-"erneut verbinden."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:238
-msgid "Avahi error"
-msgstr "Avahi-Fehler"
-
-#: ../src/common/zeroconf/connection_zeroconf.py:238
-#, python-format
-msgid ""
-"%s\n"
-"Link-local messaging might not work properly."
-msgstr ""
-"%s\n"
-"Lokaler Nachrichtenversand funktioniert eventuell nicht richtig."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:249
-#, fuzzy
-msgid "Please check if Avahi or Bonjour is installed."
-msgstr "Bitte überprüfen Sie, ob Avahi installiert ist."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:258
-#: ../src/common/zeroconf/connection_zeroconf.py:262
-msgid "Could not start local service"
-msgstr "Lokaler Dienst konnte nicht gestartet werden"
-
-#: ../src/common/zeroconf/connection_zeroconf.py:259
-#, python-format
-msgid "Unable to bind to port %d."
-msgstr "Konnte nicht mit Port %d verbinden."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:263
-#: ../src/common/zeroconf/connection_zeroconf.py:358
-msgid "Please check if avahi-daemon is running."
-msgstr "Bitte überprüfen Sie, ob avahi-daemon läuft."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:357
-#, python-format
-msgid "Could not change status of account \"%s\""
-msgstr "Der Status des Kontos \"%s\" konnte nicht geändert werden."
-
-#: ../src/common/zeroconf/connection_zeroconf.py:374
-msgid ""
-"You are not connected or not visible to others. Your message could not be "
-"sent."
-msgstr ""
-"Sie sind nicht verbunden oder für andere nicht sichtbar. Ihre Nachricht "
-"konnte nicht versendet werden."
-
-#. we're not english
-#: ../src/common/zeroconf/connection_zeroconf.py:391
-msgid "[This message is encrypted]"
-msgstr "[Diese Nachricht ist verschlüsselt]"
-
-#: ../src/common/zeroconf/connection_zeroconf.py:456
-msgid "Your message could not be sent."
-msgstr "Die Nachricht konnte nicht gesendet werden."
-
-#. Contact Offline
-#: ../src/common/zeroconf/connection_zeroconf.py:463
-msgid "Contact is offline. Your message could not be sent."
-msgstr "Kontakt ist offline. Ihre Nachricht konnte nicht versendet werden."
-
-#: ../src/common/zeroconf/zeroconf_avahi.py:183
-#: ../src/common/zeroconf/zeroconf_bonjour.py:194
-#, python-format
-msgid "Error while adding service. %s"
-msgstr "Fehler beim Hinzufügen des Dienstes. %s"
+#~ msgid "Requires pyotr and libotr."
+#~ msgstr "Erfordert pyotr und libotr."
#~ msgid "<b>History Viewer</b>"
#~ msgstr "<b>Verlauf ansehen</b>"
@@ -9158,43 +9239,6 @@ msgstr "Fehler beim Hinzufügen des Dienstes. %s"
#~ "Wenn aktiviert, kann Gajim regelmäßig ein Last.FM Konto abfragen und "
#~ "kürzlich gespielte Songs über PEP senden."
-#~ msgid "_Configure"
-#~ msgstr "_Einstellen"
-
-#~ msgid "gtk-delete"
-#~ msgstr "gtk-delete"
-
-#~ msgid ""
-#~ "To continue, Gajim needs to access your stored secrets. Enter your "
-#~ "passphrase"
-#~ msgstr ""
-#~ "Um fortzufahren benötigt Gajim Zugriff auf Ihre gespeicherten Passwörter. "
-#~ "Geben Sie Ihre Passphrase ein"
-
-#~ msgid "Confirm Passphrase"
-#~ msgstr "Passphrase bestätigen"
-
-#~ msgid "Enter your new passphrase again for confirmation"
-#~ msgstr "Passphrase zur Bestätigung erneut eingeben"
-
-#~ msgid "Create Passphrase"
-#~ msgstr "Passphrase erstellen"
-
-#~ msgid "Passphrases did not match.\n"
-#~ msgstr "Passphrasen stimmen nicht überein.\n"
-
-#~ msgid "Gajim needs you to create a passphrase to encrypt stored secrets"
-#~ msgstr ""
-#~ "Sie müssen eine Passphrase angeben um Ihre gespeicherten Passwörter zu "
-#~ "verschlüsseln"
-
-#, fuzzy
-#~ msgid "Generic"
-#~ msgstr "Allgemein"
-
-#~ msgid "Select the account with which to synchronise"
-#~ msgstr "Wählen Sie das Benutzerkonto mit welchem synchronisiert werden soll"
-
#~ msgid "%s has not broadcast an OpenPGP key, nor has one been assigned"
#~ msgstr ""
#~ "%s hat keinen OpenPGP-Schlüssel verbreitet und es wurde keiner zugewiesen"
@@ -9230,9 +9274,6 @@ msgstr "Fehler beim Hinzufügen des Dienstes. %s"
#~ "fragen, wenn Sie Ihren Status zu Abwesend ändern; es wird die hier "
#~ "definierte Nachricht verwendet."
-#~ msgid "Default Status Messages"
-#~ msgstr "Vorgegebene Status-Nachrichten"
-
#~ msgid ""
#~ "Determined by sender\n"
#~ "Chat message\n"
@@ -9254,9 +9295,6 @@ msgstr "Fehler beim Hinzufügen des Dienstes. %s"
#~ msgid "Publish _Mood"
#~ msgstr "_Veröffentliche Stimmung"
-#~ msgid "Publish _Tune"
-#~ msgstr "_Veröffentliche Musiktitel"
-
#~ msgid "Set status message to reflect currently playing _music track"
#~ msgstr "Setze Status-Nachricht auf den aktuell spielenden _Musiktitel"
@@ -9611,7 +9649,7 @@ msgstr "Fehler beim Hinzufügen des Dienstes. %s"
#~ msgid "Unable to check fingerprint for %s. Connection could be insecure."
#~ msgstr ""
-#~ "Konnte Fingerabdruck von %s nicht überprüfen. Die Verbindung ist "
+#~ "Konnte Fingerprint von %s nicht überprüfen. Die Verbindung ist "
#~ "möglicherweise unsicher."
#~ msgid "Missing fingerprint in SSL connection to %s"
diff --git a/po/fr.po b/po/fr.po
index 84af56af7..f59491067 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1297,16 +1297,15 @@ msgstr "_Gérer le Salon"
#: ../data/glade/gc_control_popup_menu.glade.h:9
msgid "_Minimize on close"
-msgstr "_Minimiser à la fermeteure"
+msgstr "_Minimiser à la fermeture"
#: ../data/glade/gc_occupants_menu.glade.h:1
msgid "Mo_derator"
msgstr "Mo_dérer"
#: ../data/glade/gc_occupants_menu.glade.h:2
-#, fuzzy
msgid "Occupant Actions"
-msgstr "Actions des occupants"
+msgstr "Actions des Occupants"
#: ../data/glade/gc_occupants_menu.glade.h:5
msgid "_Admin"
@@ -1538,52 +1537,44 @@ msgid "_Host:"
msgstr "_Serveur :"
#: ../data/glade/message_window.glade.h:1
-#, fuzzy
msgid "Add this contact to roster (Ctrl-D)"
-msgstr "Ajouter le contact à la liste"
+msgstr "Ajouter le contact à la liste (Ctrl-D)"
#: ../data/glade/message_window.glade.h:2
-#, fuzzy
msgid "Bookmark this room (Ctrl-B)"
-msgstr "Enregistrer ce salon dans les marque-pages"
+msgstr "Enregistrer ce salon dans les marque-pages (Ctrl-B)"
#: ../data/glade/message_window.glade.h:3
msgid "Browse the chat history (Ctrl-H)"
msgstr "Parcourir l'historique des conversations (Ctrl+H)"
#: ../data/glade/message_window.glade.h:4
-#, fuzzy
msgid "Change the room's subject (Ctrl-T)"
-msgstr "Changer le sujet du salon"
+msgstr "Changer le sujet du salon (Ctrl-T)"
#: ../data/glade/message_window.glade.h:5
-#, fuzzy
msgid "Change your nickname (Ctrl-N)"
-msgstr "Changer votre surnom"
+msgstr "Changer votre surnom (Ctrl-N)"
#: ../data/glade/message_window.glade.h:6
-#, fuzzy
msgid "Invite contacts to the conversation (Ctrl-G)"
-msgstr "Inviter des contacts dans la conversation"
+msgstr "Inviter des contacts dans la conversation (Ctrl-G)"
#: ../data/glade/message_window.glade.h:7
-#, fuzzy
msgid "Send a file (Ctrl-F)"
-msgstr "Envoyer un fichier"
+msgstr "Envoyer un fichier (Ctrl-F)"
#: ../data/glade/message_window.glade.h:8
msgid "Show a list of emoticons (Alt-M)"
msgstr "Afficher une liste des émoticônes (Alt+M)"
#: ../data/glade/message_window.glade.h:9
-#, fuzzy
msgid "Show a menu of advanced functions (Alt-A)"
-msgstr "Affiche un menu de fonctions avancées"
+msgstr "Affiche un menu de fonctions avancées (Alt-A)"
#: ../data/glade/message_window.glade.h:10
-#, fuzzy
msgid "Show the contact's profile (Ctrl-I)"
-msgstr "Afficher le profil du contact (Ctrl+I)"
+msgstr "Afficher le profil du contact (Ctrl-I)"
#. Make sure the character after "_" is not M/m (conflicts with Alt+M that is supposed to show the Emoticon Selector)
#: ../data/glade/message_window.glade.h:12
@@ -4509,7 +4500,6 @@ msgstr ""
"« use_latex » à True dans l'éditeur de configuration avancée."
#: ../src/features_window.py:98
-#, fuzzy
msgid "End to End Encryption"
msgstr "Chiffrement de bout en bout"
diff --git a/src/chat_control.py b/src/chat_control.py
index 9f6b9093d..127e3dc95 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -619,15 +619,16 @@ class ChatControlBase(MessageControl):
'''Send the given message to the active tab. Doesn't return None if error
'''
if not message or message == '\n':
- return 1
+ return None
+
+ ret = None
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_xep = composing_xep, resource = resource,
user_nick = self.user_nick)
- if ret:
- return ret
+
# Record message history
self.save_sent_message(message)
@@ -638,6 +639,8 @@ class ChatControlBase(MessageControl):
message_buffer = self.msg_textview.get_buffer()
message_buffer.set_text('') # clear message buffer (and tv of course)
+ return ret
+
def save_sent_message(self, message):
# save the message, so user can scroll though the list with key up/down
size = len(self.sent_history)
@@ -1112,7 +1115,9 @@ class ChatControl(ChatControlBase):
self.on_avatar_eventbox_button_press_event)
self.handlers[id] = widget
- self.set_session(session)
+ self.session = session
+ if session:
+ session.control = self
# Enable ecryption if needed
e2e_is_active = hasattr(self, 'session') and self.session and self.session.enable_encryption
@@ -1133,9 +1138,6 @@ class ChatControl(ChatControlBase):
self.status_tooltip = gtk.Tooltips()
- if gajim.otr_module:
- self.update_otr(True)
-
self.update_ui()
# restore previous conversation
self.restore_conversation()
@@ -1205,52 +1207,6 @@ class ChatControl(ChatControlBase):
# The name banner is drawn here
ChatControlBase.update_ui(self)
- def get_otr_status(self):
- if not self.session:
- return 0
-
- ctx = gajim.otr_module.otrl_context_find(
- self.session.conn.otr_userstates,
- self.contact.get_full_jid().encode(),
- gajim.get_jid_from_account(self.account).encode(),
- gajim.OTR_PROTO, 1, (gajim.otr_add_appdata,
- self.account))[0]
-
- if ctx.msgstate == gajim.otr_module.OTRL_MSGSTATE_ENCRYPTED:
- if ctx.active_fingerprint.trust:
- return 2
- else:
- return 1
- elif ctx.msgstate == gajim.otr_module.OTRL_MSGSTATE_FINISHED:
- return 3
- return 0
-
- def update_otr(self, print_status=False):
- otr_status_text = ''
- otr_status = self.get_otr_status()
- authenticated = False
-
- if otr_status > 0:
- enc_status = True
- else:
- enc_status = False
-
- if otr_status == 1:
- otr_status_text = u'*unauthenticated* secure OTR ' + \
- u'connection'
- elif otr_status == 2:
- otr_status_text = u'authenticated secure OTR ' + \
- u'connection'
- authenticated = True
- elif otr_status == 3:
- otr_status_text = u'finished OTR connection'
-
- self._show_lock_image(enc_status, u'OTR', enc_status, True,
- authenticated)
- if print_status and otr_status_text != '':
- self.print_conversation_line(u'[OTR] %s' % \
- otr_status_text, 'status', '', None)
-
def _update_banner_state_image(self):
contact = gajim.contacts.get_contact_with_highest_priority(self.account,
self.contact.jid)
@@ -1497,7 +1453,7 @@ class ChatControl(ChatControlBase):
def send_message(self, message, keyID = '', chatstate = None):
'''Send a message to contact'''
if message in ('', None, '\n') or self._process_command(message):
- return
+ return None
# Do we need to process command for the message ?
process_command = True
@@ -1540,7 +1496,7 @@ class ChatControl(ChatControlBase):
# if peer supports jep85 and we are not 'ask', send 'active'
# NOTE: first active and 'ask' is set in gajim.py
elif composing_xep is not False:
- #send active chatstate on every message (as JEP says)
+ # send active chatstate on every message (as XEP says)
chatstate_to_send = 'active'
contact.our_chatstate = 'active'
@@ -1548,8 +1504,9 @@ class ChatControl(ChatControlBase):
gobject.source_remove(self.possible_inactive_timeout_id)
self._schedule_activity_timers()
- if not ChatControlBase.send_message(self, message, keyID, type = 'chat',
- chatstate = chatstate_to_send, composing_xep = composing_xep,
+ if ChatControlBase.send_message(self, message, keyID,
+ type = 'chat', chatstate = chatstate_to_send,
+ composing_xep = composing_xep,
process_command = process_command):
self.print_conversation(message, self.contact.jid,
encrypted = encrypted)
@@ -1660,15 +1617,6 @@ class ChatControl(ChatControlBase):
'NOT encrypted')
ChatControlBase.print_conversation_line(
self, msg, 'status', '', tim)
- elif gajim.otr_module and self.get_otr_status() > 0:
- # OTR
- # TODO: This is not shown when the window
- # isn't open - needs fixing!
- if not encrypted and frm == '':
- msg = _('The following message was ' + \
- 'NOT encrypted')
- ChatControlBase.print_conversation_line(
- self, msg, 'status', '', tim)
else:
# GPG encryption
if encrypted and not self.gpg_is_active:
@@ -1794,11 +1742,6 @@ class ChatControl(ChatControlBase):
history_menuitem = xml.get_widget('history_menuitem')
toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem')
toggle_e2e_menuitem = xml.get_widget('toggle_e2e_menuitem')
- otr_submenu = xml.get_widget('otr_submenu')
- otr_settings_menuitem = xml.get_widget('otr_settings_menuitem')
- smp_otr_menuitem = xml.get_widget('smp_otr_menuitem')
- start_otr_menuitem = xml.get_widget('start_otr_menuitem')
- end_otr_menuitem = xml.get_widget('end_otr_menuitem')
send_file_menuitem = xml.get_widget('send_file_menuitem')
information_menuitem = xml.get_widget('information_menuitem')
convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat')
@@ -1891,32 +1834,6 @@ class ChatControl(ChatControlBase):
self._on_convert_to_gc_menuitem_activate)
self.handlers[id] = convert_to_gc_menuitem
- if gajim.otr_module:
- otr_submenu.set_sensitive(True)
- id = otr_settings_menuitem.connect('activate',
- self._on_otr_settings_menuitem_activate)
- self.handlers[id] = otr_settings_menuitem
- id = start_otr_menuitem.connect('activate',
- self._on_start_otr_menuitem_activate)
- self.handlers[id] = start_otr_menuitem
- id = end_otr_menuitem.connect('activate',
- self._on_end_otr_menuitem_activate)
- self.handlers[id] = end_otr_menuitem
- id = smp_otr_menuitem.connect('activate',
- self._on_smp_otr_menuitem_activate)
- self.handlers[id] = smp_otr_menuitem
-
- ctx = gajim.otr_module.otrl_context_find(gajim.connections[self.account].otr_userstates,
- self.contact.get_full_jid().encode(),
- gajim.get_jid_from_account(self.account).encode(), gajim.OTR_PROTO, 1,
- (gajim.otr_add_appdata, self.account))[0]
- # can end only when PLAINTEXT
- end_otr_menuitem.set_sensitive(ctx.msgstate !=
- gajim.otr_module.OTRL_MSGSTATE_PLAINTEXT)
- # can SMP only when ENCRYPTED
- smp_otr_menuitem.set_sensitive(ctx.msgstate ==
- gajim.otr_module.OTRL_MSGSTATE_ENCRYPTED)
-
menu.connect('selection-done', self.destroy_menu,
send_file_menuitem, convert_to_gc_menuitem,
information_menuitem, history_menuitem)
@@ -2037,7 +1954,7 @@ class ChatControl(ChatControlBase):
# Clean events
gajim.events.remove_events(self.account, self.get_full_jid(),
types = ['printed_' + self.type_id, self.type_id])
- # remove all register handlers on wigets, created by self.xml
+ # remove all register handlers on widgets, created by self.xml
# to prevent circular references among objects
for i in self.handlers.keys():
if self.handlers[i].handler_is_connected(i):
@@ -2404,28 +2321,6 @@ class ChatControl(ChatControlBase):
# XXX decide whether to use 4 or 3 message negotiation
self.session.negotiate_e2e(False)
- def _on_start_otr_menuitem_activate(self, widget):
- # ?OTR? gets replaced with a better message internally in otrl_message_sending
- MessageControl.send_message(self, u'?OTR?', type='chat')
- def _on_end_otr_menuitem_activate(self, widget):
- fjid = self.contact.get_full_jid()
- gajim.otr_module.otrl_message_disconnect(
- self.session.conn.otr_userstates, (gajim.otr_ui_ops,
- {'account': self.account, 'urgent': True}),
- gajim.get_jid_from_account(self.account).encode(),
- gajim.OTR_PROTO, fjid.encode())
- gajim.otr_ui_ops.gajim_log(_('Private conversation with ' \
- '%s lost.') % fjid, self.account, fjid.encode())
- self.update_otr()
- def _on_otr_settings_menuitem_activate(self, widget):
- gajim.otr_windows.ContactOtrWindow(self.contact, self.account, self)
- def _on_smp_otr_menuitem_activate(self, widget):
- ctx = gajim.otr_module.otrl_context_find(gajim.connections[self.account].otr_userstates,
- self.contact.get_full_jid().encode(),
- gajim.get_jid_from_account(self.account).encode(), gajim.OTR_PROTO, 1,
- (gajim.otr_add_appdata, self.account))[0]
- ctx.app_data.show(False)
-
def got_connected(self):
ChatControlBase.got_connected(self)
# Refreshing contact
diff --git a/src/common/config.py b/src/common/config.py
index 50f9e91e2..9b950705a 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -250,6 +250,7 @@ class Config:
'max_conversation_lines': [opt_int, 500, _('Maximum number of lines that are printed in conversations. Oldest lines are cleared.')],
'attach_notifications_to_systray': [opt_bool, False, _('If True, notification windows from notification-daemon will be attached to systray icon.')],
'check_idle_every_foo_seconds': [opt_int, 2, _('Choose interval between 2 checks of idleness.')],
+ 'latex_png_dpi': [opt_str, '108',_('Change the value to change the size of latex formulas displayed. The higher is larger.') ],
}
__options_per_key = {
@@ -310,7 +311,8 @@ class Config:
'zeroconf_jabber_id': [ opt_str, '', '', True ],
'zeroconf_email': [ opt_str, '', '', True ],
'use_env_http_proxy' : [opt_bool, False],
- 'otr_flags': [opt_int, 58 ],
+ 'answer_receipt' : [opt_bool, True, _('Answer to receipt requests')],
+ 'request_receipt' : [opt_bool, True, _('Sent receipt requests')],
'publish_mood': [opt_bool, True],
'publish_activity': [opt_bool, True],
'publish_tune': [opt_bool, False],
@@ -369,7 +371,6 @@ class Config:
'contacts': ({
'gpg_enabled': [ opt_bool, False, _('Is OpenPGP enabled for this contact?')],
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
- 'otr_flags': [opt_int, -1 ],
}, {}),
'rooms': ({
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
diff --git a/src/common/connection.py b/src/common/connection.py
index e9ffba607..493140fe0 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -364,9 +364,9 @@ class Connection(ConnectionHandlers):
# data is (dict)
self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
elif realm == '':
- if event == common.xmpp.transports.DATA_RECEIVED:
+ if event == common.xmpp.transports_nb.DATA_RECEIVED:
self.dispatch('STANZA_ARRIVED', unicode(data, errors = 'ignore'))
- elif event == common.xmpp.transports.DATA_SENT:
+ elif event == common.xmpp.transports_nb.DATA_SENT:
self.dispatch('STANZA_SENT', unicode(data))
def select_next_host(self, hosts):
@@ -896,20 +896,6 @@ class Connection(ConnectionHandlers):
self.on_connect_auth = self._init_roster
self.connect_and_auth()
- if gajim.otr_module:
- try:
- gajim.otr_module.otrl_privkey_read(self.otr_userstates,
- os.path.join(gajim.gajimpaths.root,
- '%s.key' % self.name).encode())
- gajim.otr_module.otrl_privkey_read_fingerprints(
- self.otr_userstates, os.path.join(
- gajim.gajimpaths.root, '%s.fpr' %
- self.name).encode(),
- (gajim.otr_add_appdata, self.name))
- except Exception, e:
- if not hasattr(e, 'os_errno') or e.os_errno != 2:
- raise
-
def _init_roster(self, con):
self.connection = con
if not self.connection:
@@ -1123,6 +1109,14 @@ class Connection(ConnectionHandlers):
namespace=common.xmpp.NS_ADDRESS)
addresses.addChild('address', attrs = {'type': 'ofrom',
'jid': forward_from})
+
+ # TODO: We should also check if the other end supports it
+ # as XEP 0184 says checking is a SHOULD. Maybe we should
+ # implement section 6 of the XEP as well?
+ if msgtxt and gajim.config.get_per('accounts', self.name,
+ 'request_receipt'):
+ msg_iq.setTag('request', namespace='urn:xmpp:receipts')
+
if session:
# XEP-0201
session.last_send = time.time()
@@ -1132,8 +1126,7 @@ class Connection(ConnectionHandlers):
if session.enable_encryption:
msg_iq = session.encrypt_stanza(msg_iq)
-
- self.connection.send(msg_iq)
+ msg_id = self.connection.send(msg_iq)
if not forward_from and session and session.is_loggable():
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\
.split()
@@ -1155,6 +1148,8 @@ class Connection(ConnectionHandlers):
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
self.dispatch('MSGSENT', (jid, msg, keyID))
+ return msg_id
+
def send_stanza(self, stanza):
''' send a stanza untouched '''
if not self.connection:
@@ -1532,6 +1527,15 @@ class Connection(ConnectionHandlers):
# disconnect from jabber server
self.connection.send(p)
+ def gc_got_disconnected(self, room_jid):
+ ''' A groupchat got disconnected. This can be or purpose or not.
+ Save the time we quit to avoid duplicate logs AND be faster than get that
+ date from DB. Save it in mem AND in a small table (with fast access)
+ '''
+ log_time = time_time()
+ self.last_history_time[room_jid] = log_time
+ gajim.logger.set_room_last_message_time(room_jid, log_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'''
if not self.connection:
diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py
index 816577f4e..25a771e05 100644
--- a/src/common/connection_handlers.py
+++ b/src/common/connection_handlers.py
@@ -50,7 +50,8 @@ if dbus_support.supported:
from music_track_listener import MusicTrackListener
from session import ChatControlSession
-import tictactoe
+
+gajim.default_session_type = ChatControlSession
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'error']
@@ -1217,9 +1218,6 @@ class ConnectionHandlersBase:
# keep track of sessions this connection has with other JIDs
self.sessions = {}
- if gajim.otr_module:
- self.otr_userstates = gajim.otr_module.otrl_userstate_create()
-
def _FeatureNegCB(self, con, stanza, session):
gajim.log.debug('FeatureNegCB')
feature = stanza.getTag(name='feature', namespace=common.xmpp.NS_FEATURE)
@@ -1301,8 +1299,9 @@ sent a message to.'''
# sessions that we haven't received a thread ID in
idless = filter(lambda s: not s.received_thread_id, sessions)
- # filter out everything exceptthe default session type
- chat_sessions = filter(lambda s: isinstance(s, ChatControlSession), idless)
+ # filter out everything except the default session type
+ p = lambda s: isinstance(s, gajim.default_session_type)
+ chat_sessions = filter(p, idless)
if chat_sessions:
# return the session that we last sent a message in
@@ -1311,10 +1310,25 @@ sent a message to.'''
else:
return None
- # if deferred is true, the thread ID we're generating is tem
+ def find_controlless_session(self, jid):
+ '''find an active session that doesn't have a control attached'''
+
+ try:
+ sessions = self.sessions[jid].values()
+
+ # filter out everything except the default session type
+ p = lambda s: isinstance(s, gajim.default_session_type)
+ chat_sessions = filter(p, sessions)
+
+ orphaned = filter(lambda s: not s.control, chat_sessions)
+
+ return orphaned[0]
+ except KeyError:
+ return None
+
def make_new_session(self, jid, thread_id=None, type='chat', cls=None):
if not cls:
- cls = ChatControlSession
+ cls = gajim.default_session_type
# determine if this session is a pm session
# if not, discard the resource
@@ -1354,6 +1368,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
except:
HAS_IDLE = False
+ self.gmail_last_tid = None
+ self.gmail_last_time = None
+
def build_http_auth_answer(self, iq_obj, answer):
if answer == 'yes':
self.connection.send(iq_obj.buildReply('result'))
@@ -1565,9 +1582,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
jid = gajim.get_jid_from_account(self.name)
gajim.log.debug('Got notification of new gmail e-mail on %s. Asking the server for more info.' % jid)
iq = common.xmpp.Iq(typ = 'get')
- iq.setAttr('id', '13')
+ iq.setID(self.connection.getAnID())
query = iq.setTag('query')
query.setNamespace(common.xmpp.NS_GMAILNOTIFY)
+ # we want only be notified about newer mails
+ if self.gmail_last_tid:
+ query.setAttr('newer-than-tid', self.gmail_last_tid)
+ if self.gmail_last_time:
+ query.setAttr('newer-than-time', self.gmail_last_time)
self.connection.send(iq)
raise common.xmpp.NodeProcessed
@@ -1584,16 +1606,34 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if gm.getTag('mailbox').getTag('mail-thread-info'):
gmail_messages = gm.getTag('mailbox').getTags('mail-thread-info')
for gmessage in gmail_messages:
- sender = gmessage.getTag('senders').getTag('sender')
- if not sender:
+ unread_senders = []
+ for sender in gmessage.getTag('senders').getTags('sender'):
+ if sender.getAttr('unread') != '1':
+ continue
+ if sender.getAttr('name'):
+ unread_senders.append(sender.getAttr('name') + '< ' + \
+ sender.getAttr('address') + '>')
+ else:
+ unread_senders.append(sender.getAttr('address'))
+
+ if not unread_senders:
continue
- gmail_from = sender.getAttr('address')
gmail_subject = gmessage.getTag('subject').getData()
gmail_snippet = gmessage.getTag('snippet').getData()
+ tid = int(gmessage.getAttr('tid'))
+ if not self.gmail_last_tid or tid > self.gmail_last_tid:
+ self.gmail_last_tid = tid
gmail_messages_list.append({ \
- 'From': gmail_from, \
+ 'From': unread_senders, \
'Subject': gmail_subject, \
- 'Snippet': gmail_snippet})
+ 'Snippet': gmail_snippet, \
+ 'url': gmessage.getAttr('url'), \
+ 'participation': gmessage.getAttr('participation'), \
+ 'messages': gmessage.getAttr('messages'), \
+ 'date': gmessage.getAttr('date')})
+ self.gmail_last_time = int(gm.getTag('mailbox').getAttr(
+ 'result-time'))
+
jid = gajim.get_jid_from_account(self.name)
gajim.log.debug(('You have %s new gmail e-mails on %s.') % (newmsgs, jid))
self.dispatch('GMAIL_NOTIFY', (jid, newmsgs, gmail_messages_list))
@@ -1650,96 +1690,21 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
encrypted = False
xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO)
-
- # We don't trust libotr, that's why we only pass the message
- # to it if necessary. otrl_proto_message_type does this check.
- if gajim.otr_module and not xep_200_encrypted \
- and isinstance(msgtxt, unicode) and \
- gajim.otr_module.otrl_proto_message_type(msgtxt.encode()) != \
- gajim.otr_module.OTRL_MSGTYPE_NOTOTR:
- # set to encrypted if it's really encrypted.
- if gajim.otr_module.otrl_proto_message_type(
- msgtxt.encode()) != \
- gajim.otr_module.OTRL_MSGTYPE_TAGGEDPLAINTEXT:
- encrypted = True
-
- # TODO: Do we really need .encode()?
- # yes we do. OTR can't handle unicode.
- otr_msg_tuple = \
- gajim.otr_module.otrl_message_receiving(
- self.otr_userstates,
- (gajim.otr_ui_ops, {'account': self.name}),
- gajim.get_jid_from_account(self.name).encode(),
- gajim.OTR_PROTO,
- frm.encode(),
- msgtxt.encode(),
- (gajim.otr_add_appdata, self.name))
- msgtxt = unicode(otr_msg_tuple[1])
-
- html_node = msg.getTag('html')
- if html_node:
- msg.delChild(html_node)
- msg.setBody(msgtxt)
-
- if gajim.otr_module.otrl_tlv_find(
- otr_msg_tuple[2],
- gajim.otr_module.OTRL_TLV_DISCONNECTED) != None:
- gajim.otr_ui_ops.gajim_log(_('%s ' \
- 'has ended his/her private ' \
- 'conversation with you. You should ' \
- 'do the same.') % frm,
- self.name,
- frm.encode())
-
- ctrls = gajim.interface.msg_win_mgr.get_chat_controls(jid, self.name)
- for ctrl in ctrls:
- ctrl.update_otr()
-
- ctx = gajim.otr_module. \
- otrl_context_find(
- self.otr_userstates,
- frm.encode(),
- gajim.get_jid_from_account(
- self.name).encode(),
- gajim.OTR_PROTO, 1,
- (gajim.otr_add_appdata,
- self.name))[0]
- tlvs = otr_msg_tuple[2]
- ctx.app_data.handle_tlv(tlvs)
-
- if msgtxt == '':
- return
- elif msgtxt != None and msgtxt != '':
- gajim.otr_dont_append_tag[frm] = True
-
- # We're also here if we just don't
- # support OTR. Thus, we should strip
- # the tags from plaintext messages
- # since they look ugly.
- msgtxt = msgtxt.replace('\x20\x09\x20' \
- '\x20\x09\x09\x09\x09\x20\x09' \
- '\x20\x09\x20\x09\x20\x20', '')
- msgtxt = msgtxt.replace('\x20\x09\x20' \
- '\x09\x20\x20\x09\x20', '')
- msgtxt = msgtxt.replace('\x20\x20\x09' \
- '\x09\x20\x20\x09\x20', '')
-
- game_invite = msg.getTag('invite', namespace='http://jabber.org/protocol/games')
- if game_invite:
- game = game_invite.getTag('game')
-
- if game.getAttr('var') == \
- 'http://jabber.org/protocol/games/tictactoe':
- cls = tictactoe.TicTacToeSession
-
- # this assumes that the invitation came with a thread_id we haven't
- # seen
- session = self.make_new_session(frm, thread_id, cls=cls)
-
- session.invited(msg)
-
- return
- elif mtype != 'groupchat':
+
+ # Receipt requested
+ # TODO: We shouldn't answer if we're invisible!
+ if msg.getTag('request', namespace='urn:xmpp:receipts') and \
+ gajim.config.get_per('accounts', self.name, 'answer_receipt') \
+ and gajim.contacts.get_contact_from_full_jid(self.name, frm). \
+ sub not in (u'to', u'none'):
+ receipt = common.xmpp.Message(to = jid, typ = 'chat')
+ receipt.setID(msg.getID())
+ receipt.setTag('received',
+ namespace='urn:xmpp:receipts')
+ receipt.setThread(thread_id)
+ con.send(receipt)
+
+ if mtype != 'groupchat':
session = self.get_or_create_session(frm, thread_id)
if thread_id and not session.received_thread_id:
@@ -1851,7 +1816,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
no_log_for = ''
no_log_for = no_log_for.split()
-
tim_int = int(float(mktime(tim)))
if self.name not in no_log_for and jid not in no_log_for and not \
@@ -1861,8 +1825,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# so don't store it in logs
try:
gajim.logger.write('gc_msg', frm, msgtxt, tim=tim)
- # save the time we log to avoid duplicate logs
- self.last_history_time[jid] = tim_int
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
diff --git a/src/common/contacts.py b/src/common/contacts.py
index 63724c238..2fa74f750 100644
--- a/src/common/contacts.py
+++ b/src/common/contacts.py
@@ -32,7 +32,7 @@ class Contact:
self.jid = jid
self.name = name
self.contact_name = '' # nick choosen by contact
- self.groups = groups
+ self.groups = groups # See below for what we do if it's empty
self.show = show
self.status = status
self.sub = sub
@@ -63,7 +63,11 @@ class Contact:
# this is contact's chatstate
self.chatstate = chatstate
self.last_status_time = last_status_time
-
+ if not self.groups:
+ if self.is_observer():
+ self.groups = [_('Observers')]
+ else:
+ self.groups = [_('General')]
self.mood = mood.copy()
self.tune = tune.copy()
self.activity = activity.copy()
@@ -246,9 +250,10 @@ class Contacts:
return None
def iter_contacts(self, account):
- for jid in self._contacts[account]:
- for contact in self._contacts[account][jid]:
- yield contact
+ if account in self._contacts:
+ for jid in self._contacts[account]:
+ for contact in self._contacts[account][jid]:
+ yield contact
def get_contact_from_full_jid(self, account, fjid):
''' Get Contact object for specific resource of given jid'''
@@ -310,18 +315,8 @@ class Contacts:
if groups == []:
in_groups = True
else:
- contact_groups = contact.groups
- if not contact_groups:
- # Contact is not in a group, so count it in General or
- # Transports group
- if common.gajim.jid_is_transport(jid):
- contact_groups = [_('Transports')]
- if contact.is_observer():
- contact_groups = [_('Observers')]
- else:
- contact_groups = [_('General')]
for group in groups:
- if group in contact_groups:
+ if group in contact.groups:
in_groups = True
break
diff --git a/src/common/dbus_support.py b/src/common/dbus_support.py
index 71695036a..b5998d2f4 100644
--- a/src/common/dbus_support.py
+++ b/src/common/dbus_support.py
@@ -29,8 +29,11 @@ _GAJIM_ERROR_IFACE = 'org.gajim.dbus.Error'
try:
if sys.platform == 'darwin':
- import osx.dbus
- osx.dbus.load(True)
+ try:
+ import osx.dbus
+ osx.dbus.load(True)
+ except ImportError:
+ pass
import dbus
import dbus.service
import dbus.glib
diff --git a/src/common/gajim.py b/src/common/gajim.py
index eaa84ec4f..110e14008 100644
--- a/src/common/gajim.py
+++ b/src/common/gajim.py
@@ -165,13 +165,6 @@ else:
if system('gpg -h >/dev/null 2>&1'):
HAVE_GPG = False
-OTR_PROTO = "xmpp"
-otr_userstates = {}
-otr_policy = {}
-
-# list of (full) jids not to attempt OTR with
-otr_dont_append_tag = {}
-
gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'}
gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI,
xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_MUC_USER,
diff --git a/src/common/helpers.py b/src/common/helpers.py
index 1f9ca45f0..d99639e4c 100644
--- a/src/common/helpers.py
+++ b/src/common/helpers.py
@@ -53,8 +53,10 @@ except ImportError:
hash_md5 = md5.new
hash_sha1 = sha.new
-if sys.platform == 'darwin':
+try:
from osx import nsapp
+except ImportError:
+ pass
try:
import winsound # windows-only built-in module for playing wav
@@ -570,7 +572,10 @@ def play_sound_file(path_to_soundfile):
if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
return
if sys.platform == 'darwin':
- nsapp.playFile(path_to_soundfile)
+ try:
+ nsapp.playFile(path_to_soundfile)
+ except NameError:
+ pass
elif os.name == 'nt':
try:
winsound.PlaySound(path_to_soundfile,
diff --git a/src/common/xmpp/__init__.py b/src/common/xmpp/__init__.py
index 4645ceda2..90f852598 100644
--- a/src/common/xmpp/__init__.py
+++ b/src/common/xmpp/__init__.py
@@ -26,7 +26,7 @@ and use only methods for access all values you should not have any problems.
"""
-import simplexml,protocol,debug,auth_nb,auth,transports,transports_nb,roster_nb,roster,dispatcher_nb,features_nb,features,browser,filetransfer,commands, idlequeue
+import simplexml,protocol,debug,auth_nb,transports_nb,roster_nb,dispatcher_nb,features_nb,idlequeue
from client_nb import *
from client import *
from protocol import *
diff --git a/src/common/xmpp/auth.py b/src/common/xmpp/auth.py
deleted file mode 100644
index 3091f5542..000000000
--- a/src/common/xmpp/auth.py
+++ /dev/null
@@ -1,306 +0,0 @@
-## auth.py
-##
-## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: auth.py,v 1.35 2006/01/18 19:26:43 normanr Exp $
-
-"""
-Provides library with all Non-SASL and SASL authentication mechanisms.
-Can be used both for client and transport authentication.
-"""
-
-from protocol import *
-from client import PlugIn
-import sha,base64,random,dispatcher
-
-import md5
-def HH(some): return md5.new(some).hexdigest()
-def H(some): return md5.new(some).digest()
-def C(some): return ':'.join(some)
-
-class NonSASL(PlugIn):
- """ Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and transport authentication."""
- def __init__(self,user,password,resource):
- """ Caches username, password and resource for auth. """
- PlugIn.__init__(self)
- self.DBG_LINE='gen_auth'
- self.user=user
- self.password=password
- self.resource=resource
-
- def plugin(self,owner):
- """ Determine the best auth method (digest/0k/plain) and use it for auth.
- Returns used method name on success. Used internally. """
- if not self.resource: return self.authComponent(owner)
- self.DEBUG('Querying server about possible auth methods','start')
- resp=owner.Dispatcher.SendAndWaitForResponse(Iq('get',NS_AUTH,payload=[Node('username',payload=[self.user])]))
- if not isResultNode(resp):
- self.DEBUG('No result node arrived! Aborting...','error')
- return
- iq=Iq(typ='set',node=resp)
- query=iq.getTag('query')
- query.setTagData('username',self.user)
- query.setTagData('resource',self.resource)
-
- if query.getTag('digest'):
- self.DEBUG("Performing digest authentication",'ok')
- query.setTagData('digest',sha.new(owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest())
- if query.getTag('password'): query.delChild('password')
- method='digest'
- elif query.getTag('token'):
- token=query.getTagData('token')
- seq=query.getTagData('sequence')
- self.DEBUG("Performing zero-k authentication",'ok')
- hash = sha.new(sha.new(self.password).hexdigest()+token).hexdigest()
- for foo in xrange(int(seq)): hash = sha.new(hash).hexdigest()
- query.setTagData('hash',hash)
- method='0k'
- else:
- self.DEBUG("Sequre methods unsupported, performing plain text authentication",'warn')
- query.setTagData('password',self.password)
- method='plain'
- resp=owner.Dispatcher.SendAndWaitForResponse(iq)
- if isResultNode(resp):
- self.DEBUG('Sucessfully authenticated with remove host.','ok')
- owner.User=self.user
- owner.Resource=self.resource
- owner._registered_name=owner.User+'@'+owner.Server+'/'+owner.Resource
- return method
- self.DEBUG('Authentication failed!','error')
-
- def authComponent(self,owner):
- """ Authenticate component. Send handshake stanza and wait for result. Returns "ok" on success. """
- self.handshake=0
- owner.send(Node(NS_COMPONENT_ACCEPT+' handshake',payload=[sha.new(owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest()]))
- owner.RegisterHandler('handshake',self.handshakeHandler,xmlns=NS_COMPONENT_ACCEPT)
- while not self.handshake:
- self.DEBUG("waiting on handshake",'notify')
- owner.Process(1)
- owner._registered_name=self.user
- if self.handshake+1: return 'ok'
-
- def handshakeHandler(self,disp,stanza):
- """ Handler for registering in dispatcher for accepting transport authentication. """
- if stanza.getName()=='handshake': self.handshake=1
- else: self.handshake=-1
-
-class SASL(PlugIn):
- """ Implements SASL authentication. """
- def __init__(self,username,password):
- PlugIn.__init__(self)
- self.username=username
- self.password=password
-
- def plugin(self,owner):
- if not self._owner.Dispatcher.Stream._document_attrs.has_key('version'): self.startsasl='not-supported'
- elif self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else: self.startsasl=None
-
- def auth(self):
- """ Start authentication. Result can be obtained via "SASL.startsasl" attribute and will be
- either "success" or "failure". Note that successfull auth will take at least
- two Dispatcher.Process() calls. """
- if self.startsasl: pass
- elif self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else: self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
-
- def plugout(self):
- """ Remove SASL handlers from owner's dispatcher. Used internally. """
- self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
- self._owner.UnregisterHandler('challenge',self.SASLHandler,xmlns=NS_SASL)
- self._owner.UnregisterHandler('failure',self.SASLHandler,xmlns=NS_SASL)
- self._owner.UnregisterHandler('success',self.SASLHandler,xmlns=NS_SASL)
-
- def FeaturesHandler(self,conn,feats):
- """ Used to determine if server supports SASL auth. Used internally. """
- if not feats.getTag('mechanisms',namespace=NS_SASL):
- self.startsasl='not-supported'
- self.DEBUG('SASL not supported by server','error')
- return
- mecs=[]
- for mec in feats.getTag('mechanisms',namespace=NS_SASL).getTags('mechanism'):
- mecs.append(mec.getData())
- self._owner.RegisterHandler('challenge',self.SASLHandler,xmlns=NS_SASL)
- self._owner.RegisterHandler('failure',self.SASLHandler,xmlns=NS_SASL)
- self._owner.RegisterHandler('success',self.SASLHandler,xmlns=NS_SASL)
- if "DIGEST-MD5" in mecs:
- node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'DIGEST-MD5'})
- elif "PLAIN" in mecs:
- sasl_data='%s\x00%s\x00%s'%(self.username+'@'+self._owner.Server,self.username,self.password)
- node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},payload=[base64.encodestring(sasl_data)])
- else:
- self.startsasl='failure'
- self.DEBUG('I can only use DIGEST-MD5 and PLAIN mecanisms.','error')
- return
- self.startsasl='in-process'
- self._owner.send(node.__str__())
- raise NodeProcessed
-
- def SASLHandler(self,conn,challenge):
- """ Perform next SASL auth step. Used internally. """
- if challenge.getNamespace()<>NS_SASL: return
- if challenge.getName()=='failure':
- self.startsasl='failure'
- try: reason=challenge.getChildren()[0]
- except: reason=challenge
- self.DEBUG('Failed SASL authentification: %s'%reason,'error')
- raise NodeProcessed
- elif challenge.getName()=='success':
- self.startsasl='success'
- self.DEBUG('Successfully authenticated with remote server.','ok')
- handlers=self._owner.Dispatcher.dumpHandlers()
- self._owner.Dispatcher.PlugOut()
- dispatcher.Dispatcher().PlugIn(self._owner)
- self._owner.Dispatcher.restoreHandlers(handlers)
- self._owner.User=self.username
- raise NodeProcessed
-########################################3333
- incoming_data=challenge.getData()
- chal={}
- data=base64.decodestring(incoming_data)
- self.DEBUG('Got challenge:'+data,'ok')
- for pair in data.split(','):
- key,value=pair.split('=', 1)
- if value[:1]=='"' and value[-1:]=='"': value=value[1:-1]
- chal[key]=value
- if chal.has_key('qop') and chal['qop']=='auth':
- resp={}
- resp['username']=self.username
- resp['realm']=self._owner.Server
- resp['nonce']=chal['nonce']
- cnonce=''
- for i in range(7):
- cnonce+=hex(int(random.random()*65536*4096))[2:]
- resp['cnonce']=cnonce
- resp['nc']=('00000001')
- resp['qop']='auth'
- resp['digest-uri']='xmpp/'+self._owner.Server
- A1=C([H(C([resp['username'],resp['realm'],self.password])),resp['nonce'],resp['cnonce']])
- A2=C(['AUTHENTICATE',resp['digest-uri']])
- response= HH(C([HH(A1),resp['nonce'],resp['nc'],resp['cnonce'],resp['qop'],HH(A2)]))
- resp['response']=response
- resp['charset']='utf-8'
- sasl_data=''
- for key in ['charset','username','realm','nonce','nc','cnonce','digest-uri','response','qop']:
- if key in ['nc','qop','response','charset']: sasl_data+="%s=%s,"%(key,resp[key])
- else: sasl_data+='%s="%s",'%(key,resp[key])
-########################################3333
- node=Node('response',attrs={'xmlns':NS_SASL},payload=[base64.encodestring(sasl_data[:-1]).replace('\r','').replace('\n','')])
- self._owner.send(node.__str__())
- elif chal.has_key('rspauth'): self._owner.send(Node('response',attrs={'xmlns':NS_SASL}).__str__())
- else:
- self.startsasl='failure'
- self.DEBUG('Failed SASL authentification: unknown challenge','error')
- raise NodeProcessed
-
-class Bind(PlugIn):
- """ Bind some JID to the current connection to allow router know of our location."""
- def __init__(self):
- PlugIn.__init__(self)
- self.DBG_LINE='bind'
- self.bound=None
-
- def plugin(self,owner):
- """ Start resource binding, if allowed at this time. Used internally. """
- if self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else: self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
-
- def plugout(self):
- """ Remove Bind handler from owner's dispatcher. Used internally. """
- self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
-
- def FeaturesHandler(self,conn,feats):
- """ Determine if server supports resource binding and set some internal attributes accordingly. """
- if not feats.getTag('bind',namespace=NS_BIND):
- self.bound='failure'
- self.DEBUG('Server does not requested binding.','error')
- return
- if feats.getTag('session',namespace=NS_SESSION): self.session=1
- else: self.session=-1
- self.bound=[]
-
- def Bind(self,resource=None):
- """ Perform binding. Use provided resource name or random (if not provided). """
- while self.bound is None and self._owner.Process(1): pass
- if resource: resource=[Node('resource',payload=[resource])]
- else: resource=[]
- resp=self._owner.SendAndWaitForResponse(Protocol('iq',typ='set',payload=[Node('bind',attrs={'xmlns':NS_BIND},payload=resource)]))
- if isResultNode(resp):
- self.bound.append(resp.getTag('bind').getTagData('jid'))
- self.DEBUG('Successfully bound %s.'%self.bound[-1],'ok')
- jid=JID(resp.getTag('bind').getTagData('jid'))
- self._owner.User=jid.getNode()
- self._owner.Resource=jid.getResource()
- resp=self._owner.SendAndWaitForResponse(Protocol('iq',typ='set',payload=[Node('session',attrs={'xmlns':NS_SESSION})]))
- if isResultNode(resp):
- self.DEBUG('Successfully opened session.','ok')
- self.session=1
- return 'ok'
- else:
- self.DEBUG('Session open failed.','error')
- self.session=0
- elif resp: self.DEBUG('Binding failed: %s.'%resp.getTag('error'),'error')
- else:
- self.DEBUG('Binding failed: timeout expired.','error')
- return ''
-
-class ComponentBind(PlugIn):
- """ ComponentBind some JID to the current connection to allow router know of our location."""
- def __init__(self):
- PlugIn.__init__(self)
- self.DBG_LINE='bind'
- self.bound=None
- self.needsUnregister=None
-
- def plugin(self,owner):
- """ Start resource binding, if allowed at this time. Used internally. """
- if self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else:
- self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
- self.needsUnregister=1
-
- def plugout(self):
- """ Remove ComponentBind handler from owner's dispatcher. Used internally. """
- if self.needsUnregister:
- self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
-
- def FeaturesHandler(self,conn,feats):
- """ Determine if server supports resource binding and set some internal attributes accordingly. """
- if not feats.getTag('bind',namespace=NS_BIND):
- self.bound='failure'
- self.DEBUG('Server does not requested binding.','error')
- return
- if feats.getTag('session',namespace=NS_SESSION): self.session=1
- else: self.session=-1
- self.bound=[]
-
- def Bind(self,domain=None):
- """ Perform binding. Use provided domain name (if not provided). """
- while self.bound is None and self._owner.Process(1): pass
- resp=self._owner.SendAndWaitForResponse(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1))
- if resp and resp.getAttr('error'):
- self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error')
- elif resp:
- self.DEBUG('Successfully bound.','ok')
- return 'ok'
- else:
- self.DEBUG('Binding failed: timeout expired.','error')
- return ''
diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py
index fa6d40ad3..b2624043f 100644
--- a/src/common/xmpp/auth_nb.py
+++ b/src/common/xmpp/auth_nb.py
@@ -19,10 +19,14 @@ Can be used both for client and transport authentication.
'''
import sys
from protocol import *
-from auth import *
from client import PlugIn
import sha,base64,random,dispatcher_nb
+import md5
+def HH(some): return md5.new(some).hexdigest()
+def H(some): return md5.new(some).digest()
+def C(some): return ':'.join(some)
+
def challenge_splitter(data):
''' Helper function that creates a dict from challenge string.
Sample chalenge string:
@@ -235,6 +239,7 @@ class NonBlockingNonSASL(PlugIn):
self.resource = resource
self.on_auth = on_auth
+
def plugin(self, owner):
''' Determine the best auth method (digest/0k/plain) and use it for auth.
Returns used method name on success. Used internally. '''
@@ -316,8 +321,24 @@ class NonBlockingNonSASL(PlugIn):
else:
self.handshake=-1
-class NonBlockingBind(Bind):
+class NonBlockingBind(PlugIn):
''' Bind some JID to the current connection to allow router know of our location.'''
+
+ def __init__(self):
+ PlugIn.__init__(self)
+ self.DBG_LINE='bind'
+ self.bound=None
+
+ def FeaturesHandler(self,conn,feats):
+ """ Determine if server supports resource binding and set some internal attributes accordingly. """
+ if not feats.getTag('bind',namespace=NS_BIND):
+ self.bound='failure'
+ self.DEBUG('Server does not requested binding.','error')
+ return
+ if feats.getTag('session',namespace=NS_SESSION): self.session=1
+ else: self.session=-1
+ self.bound=[]
+
def plugin(self, owner):
''' Start resource binding, if allowed at this time. Used internally. '''
if self._owner.Dispatcher.Stream.features:
@@ -381,10 +402,16 @@ class NonBlockingBind(Bind):
self.session = 0
self.on_bound(None)
-class NBComponentBind(ComponentBind):
+class NBComponentBind(PlugIn):
''' ComponentBind some JID to the current connection to allow
router know of our location.
'''
+ def __init__(self):
+ PlugIn.__init__(self)
+ self.DBG_LINE='bind'
+ self.bound=None
+ self.needsUnregister=None
+
def plugin(self,owner):
''' Start resource binding, if allowed at this time. Used internally. '''
if self._owner.Dispatcher.Stream.features:
@@ -427,3 +454,26 @@ class NBComponentBind(ComponentBind):
self.DEBUG('Binding failed: timeout expired.', 'error')
if self.on_bind:
self.on_bind(None)
+
+ def FeaturesHandler(self,conn,feats):
+ """ Determine if server supports resource binding and set some internal attributes accordingly. """
+ if not feats.getTag('bind',namespace=NS_BIND):
+ self.bound='failure'
+ self.DEBUG('Server does not requested binding.','error')
+ return
+ if feats.getTag('session',namespace=NS_SESSION): self.session=1
+ else: self.session=-1
+ self.bound=[]
+
+ def Bind(self,domain=None):
+ """ Perform binding. Use provided domain name (if not provided). """
+ while self.bound is None and self._owner.Process(1): pass
+ resp=self._owner.SendAndWaitForResponse(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1))
+ if resp and resp.getAttr('error'):
+ self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error')
+ elif resp:
+ self.DEBUG('Successfully bound.','ok')
+ return 'ok'
+ else:
+ self.DEBUG('Binding failed: timeout expired.','error')
+ return ''
diff --git a/src/common/xmpp/browser.py b/src/common/xmpp/browser.py
deleted file mode 100644
index d696606a8..000000000
--- a/src/common/xmpp/browser.py
+++ /dev/null
@@ -1,216 +0,0 @@
-## browser.py
-##
-## Copyright (C) 2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: browser.py,v 1.11 2005/10/07 23:17:09 normanr Exp $
-
-"""Browser module provides DISCO server framework for your application.
-This functionality can be used for very different purposes - from publishing
-software version and supported features to building of "jabber site" that users
-can navigate with their disco browsers and interact with active content.
-
-Such functionality is achieved via registering "DISCO handlers" that are
-automatically called when user requests some node of your disco tree.
-"""
-
-from dispatcher import *
-from client import PlugIn
-
-class Browser(PlugIn):
- """ WARNING! This class is for components only. It will not work in client mode!
-
- Standart xmpppy class that is ancestor of PlugIn and can be attached
- to your application.
- All processing will be performed in the handlers registered in the browser
- instance. You can register any number of handlers ensuring that for each
- node/jid combination only one (or none) handler registered.
- You can register static information or the fully-blown function that will
- calculate the answer dynamically.
- Example of static info (see JEP-0030, examples 13-14):
- # cl - your xmpppy connection instance.
- b=xmpp.browser.Browser()
- b.PlugIn(cl)
- items=[]
- item={}
- item['jid']='catalog.shakespeare.lit'
- item['node']='books'
- item['name']='Books by and about Shakespeare'
- items.append(item)
- item={}
- item['jid']='catalog.shakespeare.lit'
- item['node']='clothing'
- item['name']='Wear your literary taste with pride'
- items.append(item)
- item={}
- item['jid']='catalog.shakespeare.lit'
- item['node']='music'
- item['name']='Music from the time of Shakespeare'
- items.append(item)
- info={'ids':[], 'features':[]}
- b.setDiscoHandler({'items':items,'info':info})
-
- items should be a list of item elements.
- every item element can have any of these four keys: 'jid', 'node', 'name', 'action'
- info should be a dicionary and must have keys 'ids' and 'features'.
- Both of them should be lists:
- ids is a list of dictionaries and features is a list of text strings.
- Example (see JEP-0030, examples 1-2)
- # cl - your xmpppy connection instance.
- b=xmpp.browser.Browser()
- b.PlugIn(cl)
- items=[]
- ids=[]
- ids.append({'category':'conference','type':'text','name':'Play-Specific Chatrooms'})
- ids.append({'category':'directory','type':'chatroom','name':'Play-Specific Chatrooms'})
- features=[NS_DISCO_INFO,NS_DISCO_ITEMS,NS_MUC,NS_REGISTER,NS_SEARCH,NS_TIME,NS_VERSION]
- info={'ids':ids,'features':features}
- # info['xdata']=xmpp.protocol.DataForm() # JEP-0128
- b.setDiscoHandler({'items':[],'info':info})
- """
- def __init__(self):
- """Initialises internal variables. Used internally."""
- PlugIn.__init__(self)
- DBG_LINE='browser'
- self._exported_methods=[]
- self._handlers={'':{}}
-
- def plugin(self, owner):
- """ Registers it's own iq handlers in your application dispatcher instance.
- Used internally."""
- owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO)
- owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS)
-
- def plugout(self):
- """ Unregisters browser's iq handlers from your application dispatcher instance.
- Used internally."""
- self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO)
- self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS)
-
- def _traversePath(self,node,jid,set=0):
- """ Returns dictionary and key or None,None
- None - root node (w/o "node" attribute)
- /a/b/c - node
- /a/b/ - branch
- Set returns '' or None as the key
- get returns '' or None as the key or None as the dict.
- Used internally."""
- if self._handlers.has_key(jid): cur=self._handlers[jid]
- elif set:
- self._handlers[jid]={}
- cur=self._handlers[jid]
- else: cur=self._handlers['']
- if node is None: node=[None]
- else: node=node.replace('/',' /').split('/')
- for i in node:
- if i<>'' and cur.has_key(i): cur=cur[i]
- elif set and i<>'': cur[i]={dict:cur,str:i}; cur=cur[i]
- elif set or cur.has_key(''): return cur,''
- else: return None,None
- if cur.has_key(1) or set: return cur,1
- raise "Corrupted data"
-
- def setDiscoHandler(self,handler,node='',jid=''):
- """ This is the main method that you will use in this class.
- It is used to register supplied DISCO handler (or dictionary with static info)
- as handler of some disco tree branch.
- If you do not specify the node this handler will be used for all queried nodes.
- If you do not specify the jid this handler will be used for all queried JIDs.
-
- Usage:
- cl.Browser.setDiscoHandler(someDict,node,jid)
- or
- cl.Browser.setDiscoHandler(someDISCOHandler,node,jid)
- where
-
- someDict={
- 'items':[
- {'jid':'jid1','action':'action1','node':'node1','name':'name1'},
- {'jid':'jid2','action':'action2','node':'node2','name':'name2'},
- {'jid':'jid3','node':'node3','name':'name3'},
- {'jid':'jid4','node':'node4'}
- ],
- 'info' :{
- 'ids':[
- {'category':'category1','type':'type1','name':'name1'},
- {'category':'category2','type':'type2','name':'name2'},
- {'category':'category3','type':'type3','name':'name3'},
- ],
- 'features':['feature1','feature2','feature3','feature4'],
- 'xdata':DataForm
- }
- }
-
- and/or
-
- def someDISCOHandler(session,request,TYR):
- # if TYR=='items': # returns items list of the same format as shown above
- # elif TYR=='info': # returns info dictionary of the same format as shown above
- # else: # this case is impossible for now.
- """
- self.DEBUG('Registering handler %s for "%s" node->%s'%(handler,jid,node), 'info')
- node,key=self._traversePath(node,jid,1)
- node[key]=handler
-
- def getDiscoHandler(self,node='',jid=''):
- """ Returns the previously registered DISCO handler
- that is resonsible for this node/jid combination.
- Used internally."""
- node,key=self._traversePath(node,jid)
- if node: return node[key]
-
- def delDiscoHandler(self,node='',jid=''):
- """ Unregisters DISCO handler that is resonsible for this
- node/jid combination. When handler is unregistered the branch
- is handled in the same way that it's parent branch from this moment.
- """
- node,key=self._traversePath(node,jid)
- if node:
- handler=node[key]
- del node[dict][node[str]]
- return handler
-
- def _DiscoveryHandler(self,conn,request):
- """ Servers DISCO iq request from the remote client.
- Automatically determines the best handler to use and calls it
- to handle the request. Used internally.
- """
- handler=self.getDiscoHandler(request.getQuerynode(),request.getTo())
- if not handler:
- self.DEBUG("No Handler for request with jid->%s node->%s ns->%s"%(request.getTo(),request.getQuerynode(),request.getQueryNS()),'error')
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
- self.DEBUG("Handling request with jid->%s node->%s ns->%s"%(request.getTo(),request.getQuerynode(),request.getQueryNS()),'ok')
- rep=request.buildReply('result')
- if request.getQuerynode(): rep.setQuerynode(request.getQuerynode())
- q=rep.getTag('query')
- if request.getQueryNS()==NS_DISCO_ITEMS:
- # handler must return list: [{jid,action,node,name}]
- if type(handler)==dict: lst=handler['items']
- else: lst=handler(conn,request,'items')
- if lst==None:
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
- for item in lst: q.addChild('item',item)
- elif request.getQueryNS()==NS_DISCO_INFO:
- if type(handler)==dict: dt=handler['info']
- else: dt=handler(conn,request,'info')
- if dt==None:
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
- # handler must return dictionary:
- # {'ids':[{},{},{},{}], 'features':[fe,at,ur,es], 'xdata':DataForm}
- for id in dt['ids']: q.addChild('identity',id)
- for feature in dt['features']: q.addChild('feature',{'var':feature})
- if dt.has_key('xdata'): q.addChild(node=dt['xdata'])
- conn.send(rep)
- raise NodeProcessed
diff --git a/src/common/xmpp/client.py b/src/common/xmpp/client.py
index bc2335eff..aba778780 100644
--- a/src/common/xmpp/client.py
+++ b/src/common/xmpp/client.py
@@ -51,272 +51,38 @@ DBG_CLIENT='client'
DBG_COMPONENT='component'
class PlugIn:
- """ Common xmpppy plugins infrastructure: plugging in/out, debugging. """
- def __init__(self):
- self._exported_methods=[]
- self.DBG_LINE=self.__class__.__name__.lower()
-
- def PlugIn(self,owner):
- """ Attach to main instance and register ourself and all our staff in it. """
- self._owner=owner
- if self.DBG_LINE not in owner.debug_flags:
- owner.debug_flags.append(self.DBG_LINE)
- self.DEBUG('Plugging %s into %s'%(self,self._owner),'start')
- if owner.__dict__.has_key(self.__class__.__name__):
- return self.DEBUG('Plugging ignored: another instance already plugged.','error')
- self._old_owners_methods=[]
- for method in self._exported_methods:
- if owner.__dict__.has_key(method.__name__):
- self._old_owners_methods.append(owner.__dict__[method.__name__])
- owner.__dict__[method.__name__]=method
- owner.__dict__[self.__class__.__name__]=self
- if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner)
+ """ Common xmpppy plugins infrastructure: plugging in/out, debugging. """
+ def __init__(self):
+ self._exported_methods=[]
+ self.DBG_LINE=self.__class__.__name__.lower()
+
+ def PlugIn(self,owner):
+ """ Attach to main instance and register ourself and all our staff in it. """
+ self._owner=owner
+ if self.DBG_LINE not in owner.debug_flags:
+ owner.debug_flags.append(self.DBG_LINE)
+ self.DEBUG('Plugging %s into %s'%(self,self._owner),'start')
+ if owner.__dict__.has_key(self.__class__.__name__):
+ return self.DEBUG('Plugging ignored: another instance already plugged.','error')
+ self._old_owners_methods=[]
+ for method in self._exported_methods:
+ if owner.__dict__.has_key(method.__name__):
+ self._old_owners_methods.append(owner.__dict__[method.__name__])
+ owner.__dict__[method.__name__]=method
+ owner.__dict__[self.__class__.__name__]=self
+ if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner)
- def PlugOut(self):
- """ Unregister all our staff from main instance and detach from it. """
- self.DEBUG('Plugging %s out of %s.'%(self,self._owner),'stop')
- self._owner.debug_flags.remove(self.DBG_LINE)
- for method in self._exported_methods: del self._owner.__dict__[method.__name__]
- for method in self._old_owners_methods: self._owner.__dict__[method.__name__]=method
- del self._owner.__dict__[self.__class__.__name__]
- if self.__class__.__dict__.has_key('plugout'): return self.plugout()
- del self._owner
-
- def DEBUG(self,text,severity='info'):
- """ Feed a provided debug line to main instance's debug facility along with our ID string. """
- self._owner.DEBUG(self.DBG_LINE,text,severity)
-
-import transports,dispatcher,auth,roster
-class CommonClient:
- """ Base for Client and Component classes."""
- def __init__(self,server,port=5222,debug=['always', 'nodebuilder'],caller=None):
- """ Caches server name and (optionally) port to connect to. "debug" parameter specifies
- the debug IDs that will go into debug output. You can either specifiy an "include"
- or "exclude" list. The latter is done via adding "always" pseudo-ID to the list.
- Full list: ['nodebuilder', 'dispatcher', 'gen_auth', 'SASL_auth', 'bind', 'socket',
- 'CONNECTproxy', 'TLS', 'roster', 'browser', 'ibb'] . """
- if self.__class__.__name__=='Client': self.Namespace,self.DBG='jabber:client',DBG_CLIENT
- elif self.__class__.__name__=='Component': self.Namespace,self.DBG=dispatcher.NS_COMPONENT_ACCEPT,DBG_COMPONENT
- self.defaultNamespace=self.Namespace
- self.disconnect_handlers=[]
- self.Server=server
- self.Port=port
- # Who initiated this client
- # Used to register the EventDispatcher
- self._caller=caller
- if debug and type(debug)<>list: debug=['always', 'nodebuilder']
- self._DEBUG=Debug.Debug(debug)
- self.DEBUG=self._DEBUG.Show
- self.debug_flags=self._DEBUG.debug_flags
- self.debug_flags.append(self.DBG)
- self._owner=self
- self._registered_name=None
- self.RegisterDisconnectHandler(self.DisconnectHandler)
- self.connected=''
- self._component=0
-
- def RegisterDisconnectHandler(self,handler):
- """ Register handler that will be called on disconnect."""
- self.disconnect_handlers.append(handler)
-
- def UnregisterDisconnectHandler(self,handler):
- """ Unregister handler that is called on disconnect."""
- self.disconnect_handlers.remove(handler)
-
- def disconnected(self):
- """ Called on disconnection. Calls disconnect handlers and cleans things up. """
- self.connected=''
- self.DEBUG(self.DBG,'Disconnect detected','stop')
- self.disconnect_handlers.reverse()
- for i in self.disconnect_handlers: i()
- self.disconnect_handlers.reverse()
- if self.__dict__.has_key('TLS'): self.TLS.PlugOut()
-
- def DisconnectHandler(self):
- """ Default disconnect handler. Just raises an IOError.
- If you choosed to use this class in your production client,
- override this method or at least unregister it. """
- raise IOError('Disconnected from server.')
-
- def event(self,eventName,args={}):
- """ Default event handler. To be overriden. """
- print "Event: ",(eventName,args)
-
- def isConnected(self):
- """ Returns connection state. F.e.: None / 'tls' / 'tcp+non_sasl' . """
- return self.connected
-
- def reconnectAndReauth(self):
- """ Example of reconnection method. In fact, it can be used to batch connection and auth as well. """
- handlerssave=self.Dispatcher.dumpHandlers()
- self.Dispatcher.PlugOut()
- if self.__dict__.has_key('NonSASL'): self.NonSASL.PlugOut()
- if self.__dict__.has_key('SASL'): self.SASL.PlugOut()
- if self.__dict__.has_key('TLS'): self.TLS.PlugOut()
- if self.__dict__.has_key('HTTPPROXYsocket'): self.HTTPPROXYsocket.PlugOut()
- if self.__dict__.has_key('TCPsocket'): self.TCPsocket.PlugOut()
- if not self.connect(server=self._Server,proxy=self._Proxy): return
- if not self.auth(self._User,self._Password,self._Resource): return
- self.Dispatcher.restoreHandlers(handlerssave)
- return self.connected
-
- def get_peerhost(self):
- ''' get the ip address of the account, from which is made connection
- to the server , (e.g. me).
- We will create listening socket on the same ip '''
- if hasattr(self, 'Connection'):
- return self.Connection._sock.getsockname()
-
- def connect(self,server=None,proxy=None,ssl=None,use_srv=None):
- """ Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream.
- Returns None or 'tcp' or 'tls', depending on the result."""
- if not server: server=(self.Server,self.Port)
- if proxy: socket=transports.HTTPPROXYsocket(proxy,server,use_srv)
- else: socket=transports.TCPsocket(server,use_srv)
- connected=socket.PlugIn(self)
- if not connected:
- socket.PlugOut()
- return
- self._Server,self._Proxy=server,proxy
- self.connected='tcp'
- if (ssl is None and self.Connection.getPort() in (5223, 443)) or ssl:
- try: # FIXME. This should be done in transports.py
- transports.TLS().PlugIn(self,now=1)
- self.connected='ssl'
- except socket.sslerror:
- return
- dispatcher.Dispatcher().PlugIn(self)
- while self.Dispatcher.Stream._document_attrs is None:
- if not self.Process(1): return
- if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0':
- while not self.Dispatcher.Stream.features and self.Process(): pass # If we get version 1.0 stream the features tag MUST BE presented
- return self.connected
-
-class Client(CommonClient):
- """ Example client class, based on CommonClient. """
- def connect(self,server=None,proxy=None,secure=None,use_srv=True):
- """ Connect to jabber server. If you want to specify different ip/port to connect to you can
- pass it as tuple as first parameter. If there is HTTP proxy between you and server
- specify it's address and credentials (if needed) in the second argument.
- If you want ssl/tls support to be discovered and enable automatically - leave third argument as None. (ssl will be autodetected only if port is 5223 or 443)
- If you want to force SSL start (i.e. if port 5223 or 443 is remapped to some non-standard port) then set it to 1.
- If you want to disable tls/ssl support completely, set it to 0.
- Example: connect(('192.168.5.5',5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'})
- Returns '' or 'tcp' or 'tls', depending on the result."""
- if not CommonClient.connect(self,server,proxy,secure,use_srv) or secure<>None and not secure: return self.connected
- transports.TLS().PlugIn(self)
- if not self.Dispatcher.Stream._document_attrs.has_key('version') or not self.Dispatcher.Stream._document_attrs['version']=='1.0': return self.connected
- while not self.Dispatcher.Stream.features and self.Process(): pass # If we get version 1.0 stream the features tag MUST BE presented
- if not self.Dispatcher.Stream.features.getTag('starttls'): return self.connected # TLS not supported by server
- while not self.TLS.starttls and self.Process(): pass
- if not hasattr(self, 'TLS') or self.TLS.starttls!='success': self.event('tls_failed'); return self.connected
- self.connected='tls'
- return self.connected
-
- def auth(self,user,password,resource='',sasl=1):
- """ Authenticate connnection and bind resource. If resource is not provided
- random one or library name used. """
- self._User,self._Password,self._Resource=user,password,resource
- while not self.Dispatcher.Stream._document_attrs and self.Process(): pass
- if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0':
- while not self.Dispatcher.Stream.features and self.Process(): pass # If we get version 1.0 stream the features tag MUST BE presented
- if sasl: auth.SASL(user,password).PlugIn(self)
- if not sasl or self.SASL.startsasl=='not-supported':
- if not resource: resource='xmpppy'
- if auth.NonSASL(user,password,resource).PlugIn(self):
- self.connected+='+old_auth'
- return 'old_auth'
- return
- self.SASL.auth()
- while self.SASL.startsasl=='in-process' and self.Process(): pass
- if self.SASL.startsasl=='success':
- auth.Bind().PlugIn(self)
- while self.Bind.bound is None and self.Process(): pass
- if self.Bind.Bind(resource):
- self.connected+='+sasl'
- return 'sasl'
-
- def initRoster(self):
- """ Plug in the roster. """
- if not self.__dict__.has_key('Roster'): roster.Roster().PlugIn(self)
-
- def getRoster(self):
- """ Return the Roster instance, previously plugging it in and
- requesting roster from server if needed. """
- self.initRoster()
- return self.Roster.getRoster()
-
- def sendInitPresence(self,requestRoster=1):
- """ Send roster request and initial <presence/>.
- You can disable the first by setting requestRoster argument to 0. """
- self.sendPresence(requestRoster=requestRoster)
-
- def sendPresence(self,jid=None,typ=None,requestRoster=0):
- """ Send some specific presence state.
- Can also request roster from server if according agrument is set."""
- if requestRoster: roster.Roster().PlugIn(self)
- self.send(dispatcher.Presence(to=jid, typ=typ))
-
-class Component(CommonClient):
- """ Component class. The only difference from CommonClient is ability to perform component authentication. """
- def __init__(self,server,port=5347,typ=None,debug=['always', 'nodebuilder'],domains=None,component=0):
- """ Init function for Components.
- As components use a different auth mechanism which includes the namespace of the component.
- Jabberd1.4 and Ejabberd use the default namespace then for all client messages.
- Jabberd2 uses jabber:client.
- 'server' argument is a server name that you are connecting to (f.e. "localhost").
- 'port' can be specified if 'server' resolves to correct IP. If it is not then you'll need to specify IP
- and port while calling "connect()"."""
- CommonClient.__init__(self,server,port=port,debug=debug)
- self.typ=typ
- self.component=component
- if domains:
- self.domains=domains
- else:
- self.domains=[server]
-
- def connect(self,server=None,proxy=None):
- """ This will connect to the server, and if the features tag is found then set
- the namespace to be jabber:client as that is required for jabberd2.
- 'server' and 'proxy' arguments have the same meaning as in xmpp.Client.connect() """
- if self.component:
- self.Namespace=auth.NS_COMPONENT_1
- self.Server=server[0]
- CommonClient.connect(self,server=server,proxy=proxy)
- if self.connected and (self.typ=='jabberd2' or not self.typ and self.Dispatcher.Stream.features is not None):
- self.defaultNamespace=auth.NS_CLIENT
- self.Dispatcher.RegisterNamespace(self.defaultNamespace)
- self.Dispatcher.RegisterProtocol('iq',dispatcher.Iq)
- self.Dispatcher.RegisterProtocol('message',dispatcher.Message)
- self.Dispatcher.RegisterProtocol('presence',dispatcher.Presence)
- return self.connected
+ def PlugOut(self):
+ """ Unregister all our staff from main instance and detach from it. """
+ self.DEBUG('Plugging %s out of %s.'%(self,self._owner),'stop')
+ self._owner.debug_flags.remove(self.DBG_LINE)
+ for method in self._exported_methods: del self._owner.__dict__[method.__name__]
+ for method in self._old_owners_methods: self._owner.__dict__[method.__name__]=method
+ del self._owner.__dict__[self.__class__.__name__]
+ if self.__class__.__dict__.has_key('plugout'): return self.plugout()
+ del self._owner
+
+ def DEBUG(self,text,severity='info'):
+ """ Feed a provided debug line to main instance's debug facility along with our ID string. """
+ self._owner.DEBUG(self.DBG_LINE,text,severity)
- def auth(self,name,password,dup=None,sasl=0):
- """ Authenticate component "name" with password "password"."""
- self._User,self._Password,self._Resource=name,password,''
- try:
- if self.component: sasl=1
- if sasl: auth.SASL(name,password).PlugIn(self)
- if not sasl or self.SASL.startsasl=='not-supported':
- if auth.NonSASL(name,password,'').PlugIn(self):
- self.connected+='+old_auth'
- return 'old_auth'
- return
- self.SASL.auth()
- while self.SASL.startsasl=='in-process' and self.Process(): pass
- if self.SASL.startsasl=='success':
- if self.component:
- self._component=self.component
- for domain in self.domains:
- auth.ComponentBind().PlugIn(self)
- while self.ComponentBind.bound is None: self.Process()
- if (not self.ComponentBind.Bind(domain)):
- self.ComponentBind.PlugOut()
- return
- self.ComponentBind.PlugOut()
- self.connected+='+sasl'
- return 'sasl'
- else:
- raise auth.NotAuthorized(self.SASL.startsasl)
- except:
- self.DEBUG(self.DBG,"Failed to authenticate %s"%name,'error')
diff --git a/src/common/xmpp/client_bosh.py b/src/common/xmpp/client_bosh.py
deleted file mode 100644
index c2003b375..000000000
--- a/src/common/xmpp/client_bosh.py
+++ /dev/null
@@ -1,194 +0,0 @@
-## client_bosh.py
-##
-## Copyright (C) 2008 Tomas Karasek <tom.to.the.k@gmail.com>
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2, or (at your option)
-## any later version.
-##
-## 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 locale, random
-import protocol
-import simplexml
-import debug
-import dispatcher_nb
-from client_nb import NBCommonClient
-
-DBG_BOSHCLIENT='boshclient'
-
-class BOSHClient(NBCommonClient):
- '''
- BOSH (XMPP over HTTP) client implementation. It should provide the same
- methods and functionality as NonBlockingClient.
- '''
-
-
- def __init__(self, server, bosh_conn_mgr, port=5222, bosh_port=5280,
- on_connect=None, on_connect_failure=None, on_proxy_failure=None, caller=None):
- '''
- Class constuctor has the same parameters as NBCommonClient plus bosh_conn_mgr
- and bosh_port - Connection manager address and port. bosh_conn_mgr should be
- in form: 'http://httpcm.jabber.org/http-bind/'
- Tcp connection will be opened to bosh_conn_mgr:bosh_port instead of
- server:port.
- '''
- self.bosh_protocol, self.bosh_host, self.bosh_uri = self.urisplit(bosh_conn_mgr)
- if self.bosh_protocol is None:
- self.bosh_protocol = 'http'
-
- self.bosh_port = bosh_port
-
- if self.bosh_uri == '':
- bosh_uri = '/'
-
- self.xmpp_server = server
- self.xmpp_port = port
-
- self.bosh_hold = 1
- self.bosh_wait=60
- self.bosh_rid=-1
- self.bosh_httpversion = 'HTTP/1.1'
-
- NBCommonClient.__init__(self, self.bosh_host, self.bosh_port, caller=caller,
- on_connect=on_connect, on_connect_failure=on_connect_failure,
- on_proxy_failure=on_proxy_failure)
-
- # Namespace and DBG are detected in NBCommonClient constructor
- # with isinstance(). Since BOSHClient is descendant of NBCommonClient
- # and client_bosh.py is NOT imported in client_nb.py, NB_COMPONENT_ACCEPT
- # is put to namespace. This is not very nice, thus:
- # TODO: refactor Namespace and DBG recognition in NBCommonClient or
- # derived classes
- self.Namespace, self.DBG = protocol.NS_HTTP_BIND, DBG_BOSHCLIENT
- # pop of DBG_COMPONENT
- self.debug_flags.pop()
- self.debug_flags.append(self.DBG)
- self.debug_flags.append(simplexml.DBG_NODEBUILDER)
-
-
- def urisplit(self, uri):
- '''
- Function for splitting URI string to tuple (protocol, host, path).
- e.g. urisplit('http://httpcm.jabber.org/webclient') returns
- ('http', 'httpcm.jabber.org', '/webclient')
- '''
- import re
- regex = '(([^:/]+)(://))?([^/]*)(/?.*)'
- grouped = re.match(regex, uri).groups()
- proto, host, path = grouped[1], grouped[3], grouped[4]
- return proto, host, path
-
-
- def _on_connected(self):
- '''
- method called after socket starts connecting from NonBlockingTcp._do_connect
- '''
- self.onreceive(self.on_bosh_session_init_response)
- dispatcher_nb.Dispatcher().PlugIn(self)
-
-
- def parse_http_message(self, message):
- '''
- splits http message to tuple (
- statusline - list of e.g. ['HTTP/1.1', '200', 'OK'],
- headers - dictionary of headers e.g. {'Content-Length': '604',
- 'Content-Type': 'text/xml; charset=utf-8'},
- httpbody - string with http body
- )
- '''
- message = message.replace('\r','')
- (header, httpbody) = message.split('\n\n')
- header = header.split('\n')
- statusline = header[0].split(' ')
- header = header[1:]
- headers = {}
- for dummy in header:
- row = dummy.split(' ',1)
- headers[row[0][:-1]] = row[1]
- return (statusline, headers, httpbody)
-
-
- def on_bosh_session_init_response(self, data):
- '''
- Called on init response - should check relevant attributes from body tag
- '''
- if data:
- statusline, headers, httpbody = self.parse_http_message(data)
-
- if statusline[1] != '200':
- self.DEBUG(self.DBG, "HTTP Error in received session init response: %s"
- % statusline, 'error')
- # error handling TBD!
-
- # ATM, whole <body> tag is pass to ProcessNonBocking.
- # Question is how to peel off the body tag from incoming stanzas and make
- # use of ordinar xmpp traffic handling.
- self.Dispatcher.ProcessNonBlocking(httpbody)
-
-
- def _check_stream_start(self, ns, tag, attrs):
- '''
- callback stub called from XML Parser when <stream..> is discovered
- '''
- self.DEBUG(self.DBG, 'CHECK_STREAM_START: ns: %s, tag: %s, attrs: %s'
- % (ns, tag, attrs), 'info')
-
-
- def StreamInit(self):
- '''
- Initiation of BOSH session. Called instead of Dispatcher.StreamInit()
- Initial body tag is created and sent to Conn Manager.
- '''
- self.Dispatcher.Stream = simplexml.NodeBuilder()
- self.Dispatcher.Stream._dispatch_depth = 2
- self.Dispatcher.Stream.dispatch = self.Dispatcher.dispatch
- self.Dispatcher.Stream.stream_header_received = self._check_stream_start
- self.debug_flags.append(simplexml.DBG_NODEBUILDER)
- self.Dispatcher.Stream.DEBUG = self.DEBUG
- self.Dispatcher.Stream.features = None
-
- initial_body_tag = simplexml.Node('body')
- initial_body_tag.setNamespace(self.Namespace)
- initial_body_tag.setAttr('content', 'text/xml; charset=utf-8')
- initial_body_tag.setAttr('hold', str(self.bosh_hold))
- initial_body_tag.setAttr('to', self.xmpp_server)
- initial_body_tag.setAttr('wait', str(self.bosh_wait))
-
- r = random.Random()
- r.seed()
- # with 50-bit random initial rid, session would have to go up
- # to 7881299347898368 messages to raise rid over 2**53
- # (see http://www.xmpp.org/extensions/xep-0124.html#rids)
- self.bosh_rid = r.getrandbits(50)
- initial_body_tag.setAttr('rid', str(self.bosh_rid))
-
- if locale.getdefaultlocale()[0]:
- initial_body_tag.setAttr('xml:lang',
- locale.getdefaultlocale()[0].split('_')[0])
- initial_body_tag.setAttr('xmpp:version', '1.0')
- initial_body_tag.setAttr('xmlns:xmpp', 'urn:xmpp:xbosh')
-
- self.send(self.build_bosh_message(initial_body_tag))
-
-
- def build_bosh_message(self, httpbody):
- '''
- Builds bosh http message with given body.
- Values for headers and status line fields are taken from class variables.
- )
- '''
- headers = ['POST %s HTTP/1.1' % self.bosh_uri,
- 'Host: %s' % self.bosh_host,
- 'Content-Type: text/xml; charset=utf-8',
- 'Content-Length: %s' % len(str(httpbody)),
- '\r\n']
- headers = '\r\n'.join(headers)
- return('%s%s\r\n' % (headers, httpbody))
-
-
-
diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py
index ef8cfbf9e..d82fb9e94 100644
--- a/src/common/xmpp/client_nb.py
+++ b/src/common/xmpp/client_nb.py
@@ -1,8 +1,8 @@
## client_nb.py
-## based on client.py
+## based on client.py
##
## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-## modified by Dimitur Kirov <dkirov@gmail.com>
+## modified by Dimitur Kirov <dkirov@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -29,7 +29,7 @@ import debug
import transports_nb, dispatcher_nb, auth_nb, roster_nb
from client import *
-class NBCommonClient(CommonClient):
+class NBCommonClient:
''' Base for Client and Component classes.'''
def __init__(self, server, port=5222, debug=['always', 'nodebuilder'], caller=None,
on_connect=None, on_proxy_failure=None, on_connect_failure=None):
@@ -185,6 +185,37 @@ class NBCommonClient(CommonClient):
self.on_stream_start = None
return True
+ # moved from client.CommonClient:
+ def RegisterDisconnectHandler(self,handler):
+ """ Register handler that will be called on disconnect."""
+ self.disconnect_handlers.append(handler)
+
+ def UnregisterDisconnectHandler(self,handler):
+ """ Unregister handler that is called on disconnect."""
+ self.disconnect_handlers.remove(handler)
+
+ def DisconnectHandler(self):
+ """ Default disconnect handler. Just raises an IOError.
+ If you choosed to use this class in your production client,
+ override this method or at least unregister it. """
+ raise IOError('Disconnected from server.')
+
+ def event(self,eventName,args={}):
+ """ Default event handler. To be overriden. """
+ print "Event: ",(eventName,args)
+
+ def isConnected(self):
+ """ Returns connection state. F.e.: None / 'tls' / 'tcp+non_sasl' . """
+ return self.connected
+
+ def get_peerhost(self):
+ ''' get the ip address of the account, from which is made connection
+ to the server , (e.g. me).
+ We will create listening socket on the same ip '''
+ # moved from client.CommonClient
+ if hasattr(self, 'Connection'):
+ return self.Connection._sock.getsockname()
+
class NonBlockingClient(NBCommonClient):
''' Example client class, based on CommonClient. '''
def connect(self,server=None,proxy=None,secure=None,use_srv=True):
diff --git a/src/common/xmpp/commands.py b/src/common/xmpp/commands.py
deleted file mode 100644
index 433faee86..000000000
--- a/src/common/xmpp/commands.py
+++ /dev/null
@@ -1,330 +0,0 @@
-## $Id: commands.py,v 1.11 2005/11/30 17:03:11 normanr Exp $
-
-## Ad-Hoc Command manager
-## Mike Albon (c) 5th January 2005
-
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-
-"""This module is a ad-hoc command processor for xmpppy. It uses the plug-in mechanism like most of the core library. It depends on a DISCO browser manager.
-
-There are 3 classes here, a command processor Commands like the Browser, and a command template plugin Command, and an example command.
-
-To use this module:
-
- Instansiate the module with the parent transport and disco browser manager as parameters.
- 'Plug in' commands using the command template.
- The command feature must be added to existing disco replies where neccessary.
-
-What it supplies:
-
- Automatic command registration with the disco browser manager.
- Automatic listing of commands in the public command list.
- A means of handling requests, by redirection though the command manager.
-"""
-
-from protocol import *
-from client import PlugIn
-
-class Commands(PlugIn):
- """Commands is an ancestor of PlugIn and can be attached to any session.
-
- The commands class provides a lookup and browse mechnism. It follows the same priciple of the Browser class, for Service Discovery to provide the list of commands, it adds the 'list' disco type to your existing disco handler function.
-
- How it works:
- The commands are added into the existing Browser on the correct nodes. When the command list is built the supplied discovery handler function needs to have a 'list' option in type. This then gets enumerated, all results returned as None are ignored.
- The command executed is then called using it's Execute method. All session management is handled by the command itself.
- """
- def __init__(self, browser):
- """Initialises class and sets up local variables"""
- PlugIn.__init__(self)
- DBG_LINE='commands'
- self._exported_methods=[]
- self._handlers={'':{}}
- self._browser = browser
-
- def plugin(self, owner):
- """Makes handlers within the session"""
- # Plug into the session and the disco manager
- # We only need get and set, results are not needed by a service provider, only a service user.
- owner.RegisterHandler('iq',self._CommandHandler,typ='set',ns=NS_COMMANDS)
- owner.RegisterHandler('iq',self._CommandHandler,typ='get',ns=NS_COMMANDS)
- self._browser.setDiscoHandler(self._DiscoHandler,node=NS_COMMANDS,jid='')
-
- def plugout(self):
- """Removes handlers from the session"""
- # unPlug from the session and the disco manager
- self._owner.UnregisterHandler('iq',self_CommandHandler,ns=NS_COMMANDS)
- for jid in self._handlers:
- self._browser.delDiscoHandler(self._DiscoHandler,node=NS_COMMANDS)
-
- def _CommandHandler(self,conn,request):
- """The internal method to process the routing of command execution requests"""
- # This is the command handler itself.
- # We must:
- # Pass on command execution to command handler
- # (Do we need to keep session details here, or can that be done in the command?)
- jid = str(request.getTo())
- try:
- node = request.getTagAttr('command','node')
- except:
- conn.send(Error(request,ERR_BAD_REQUEST))
- raise NodeProcessed
- if self._handlers.has_key(jid):
- if self._handlers[jid].has_key(node):
- self._handlers[jid][node]['execute'](conn,request)
- else:
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
- elif self._handlers[''].has_key(node):
- self._handlers[''][node]['execute'](conn,request)
- else:
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
-
- def _DiscoHandler(self,conn,request,typ):
- """The internal method to process service discovery requests"""
- # This is the disco manager handler.
- if typ == 'items':
- # We must:
- # Generate a list of commands and return the list
- # * This handler does not handle individual commands disco requests.
- # Pseudo:
- # Enumerate the 'item' disco of each command for the specified jid
- # Build responce and send
- # To make this code easy to write we add an 'list' disco type, it returns a tuple or 'none' if not advertised
- list = []
- items = []
- jid = str(request.getTo())
- # Get specific jid based results
- if self._handlers.has_key(jid):
- for each in self._handlers[jid].keys():
- items.append((jid,each))
- else:
- # Get generic results
- for each in self._handlers[''].keys():
- items.append(('',each))
- if items != []:
- for each in items:
- i = self._handlers[each[0]][each[1]]['disco'](conn,request,'list')
- if i is not None:
- list.append(Node(tag='item',attrs={'jid':i[0],'node':i[1],'name':i[2]}))
- iq = request.buildReply('result')
- if request.getQuerynode(): iq.setQuerynode(request.getQuerynode())
- iq.setQueryPayload(list)
- conn.send(iq)
- else:
- conn.send(Error(request,ERR_ITEM_NOT_FOUND))
- raise NodeProcessed
- elif typ == 'info':
- return {'ids':[{'category':'automation','type':'command-list'}],'features':[]}
-
- def addCommand(self,name,cmddisco,cmdexecute,jid=''):
- """The method to call if adding a new command to the session, the requred parameters of cmddisco and cmdexecute are the methods to enable that command to be executed"""
- # This command takes a command object and the name of the command for registration
- # We must:
- # Add item into disco
- # Add item into command list
- if not self._handlers.has_key(jid):
- self._handlers[jid]={}
- self._browser.setDiscoHandler(self._DiscoHandler,node=NS_COMMANDS,jid=jid)
- if self._handlers[jid].has_key(name):
- raise NameError,'Command Exists'
- else:
- self._handlers[jid][name]={'disco':cmddisco,'execute':cmdexecute}
- # Need to add disco stuff here
- self._browser.setDiscoHandler(cmddisco,node=name,jid=jid)
-
- def delCommand(self,name,jid=''):
- """Removed command from the session"""
- # This command takes a command object and the name used for registration
- # We must:
- # Remove item from disco
- # Remove item from command list
- if not self._handlers.has_key(jid):
- raise NameError,'Jid not found'
- if not self._handlers[jid].has_key(name):
- raise NameError, 'Command not found'
- else:
- #Do disco removal here
- command = self.getCommand(name,jid)['disco']
- del self._handlers[jid][name]
- self._browser.delDiscoHandler(command,node=name,jid=jid)
-
- def getCommand(self,name,jid=''):
- """Returns the command tuple"""
- # This gets the command object with name
- # We must:
- # Return item that matches this name
- if not self._handlers.has_key(jid):
- raise NameError,'Jid not found'
- elif not self._handlers[jid].has_key(name):
- raise NameError,'Command not found'
- else:
- return self._handlers[jid][name]
-
-class Command_Handler_Prototype(PlugIn):
- """This is a prototype command handler, as each command uses a disco method
- and execute method you can implement it any way you like, however this is
- my first attempt at making a generic handler that you can hang process
- stages on too. There is an example command below.
-
- The parameters are as follows:
- name : the name of the command within the jabber environment
- description : the natural language description
- discofeatures : the features supported by the command
- initial : the initial command in the from of {'execute':commandname}
-
- All stages set the 'actions' dictionary for each session to represent the possible options available.
- """
- name = 'examplecommand'
- count = 0
- description = 'an example command'
- discofeatures = [NS_COMMANDS,NS_DATA]
- # This is the command template
- def __init__(self,jid=''):
- """Set up the class"""
- PlugIn.__init__(self)
- DBG_LINE='command'
- self.sessioncount = 0
- self.sessions = {}
- # Disco information for command list pre-formatted as a tuple
- self.discoinfo = {'ids':[{'category':'automation','type':'command-node','name':self.description}],'features': self.discofeatures}
- self._jid = jid
-
- def plugin(self,owner):
- """Plug command into the commands class"""
- # The owner in this instance is the Command Processor
- self._commands = owner
- self._owner = owner._owner
- self._commands.addCommand(self.name,self._DiscoHandler,self.Execute,jid=self._jid)
-
- def plugout(self):
- """Remove command from the commands class"""
- self._commands.delCommand(name,self._jid)
-
- def getSessionID(self):
- """Returns an id for the command session"""
- self.count = self.count+1
- return 'cmd-%s-%d'%(self.name,self.count)
-
- def Execute(self,conn,request):
- """The method that handles all the commands, and routes them to the correct method for that stage."""
- # New request or old?
- try:
- session = request.getTagAttr('command','sessionid')
- except:
- session = None
- try:
- action = request.getTagAttr('command','action')
- except:
- action = None
- if action is None: action = 'execute'
- # Check session is in session list
- if self.sessions.has_key(session):
- if self.sessions[session]['jid']==request.getFrom():
- # Check action is vaild
- if self.sessions[session]['actions'].has_key(action):
- # Execute next action
- self.sessions[session]['actions'][action](conn,request)
- else:
- # Stage not presented as an option
- self._owner.send(Error(request,ERR_BAD_REQUEST))
- raise NodeProcessed
- else:
- # Jid and session don't match. Go away imposter
- self._owner.send(Error(request,ERR_BAD_REQUEST))
- raise NodeProcessed
- elif session is not None:
- # Not on this sessionid you won't.
- self._owner.send(Error(request,ERR_BAD_REQUEST))
- raise NodeProcessed
- else:
- # New session
- self.initial[action](conn,request)
-
- def _DiscoHandler(self,conn,request,type):
- """The handler for discovery events"""
- if type == 'list':
- return (request.getTo(),self.name,self.description)
- elif type == 'items':
- return []
- elif type == 'info':
- return self.discoinfo
-
-class TestCommand(Command_Handler_Prototype):
- """ Example class. You should read source if you wish to understate how it works.
- Generally, it presents a "master" that giudes user through to calculate something.
- """
- name = 'testcommand'
- description = 'a noddy example command'
- def __init__(self,jid=''):
- """ Init internal constants. """
- Command_Handler_Prototype.__init__(self,jid)
- self.pi = 3.14
- self.initial = {'execute':self.cmdFirstStage}
-
- def cmdFirstStage(self,conn,request):
- """ Determine """
- # This is the only place this should be repeated as all other stages should have SessionIDs
- try:
- session = request.getTagAttr('command','sessionid')
- except:
- session = None
- if session is None:
- session = self.getSessionID()
- sessions[session]={'jid':request.getFrom(),'actions':{'cancel':self.cmdCancel,'next':self.cmdSecondStage},'data':{'type':None}}
- # As this is the first stage we only send a form
- reply = request.buildReply('result')
- form = DataForm(title='Select type of operation',data=['Use the combobox to select the type of calculation you would like to do, then click Next',DataField(name='calctype',label='Calculation Type',value=sessions[session]['data']['type'],options=[['circlediameter','Calculate the Diameter of a circle'],['circlearea','Calculate the area of a circle']],typ='list-single',required=1)])
- replypayload = [Node('actions',attrs={'execute':'next'},payload=[Node('next')]),form]
- reply.addChild(name='command',attrs={'xmlns':NS_COMMAND,'node':request.getTagAttr('command','node'),'sessionid':session,'status':'executing'},payload=replypayload)
- self._owner.send(reply)
- raise NodeProcessed
-
- def cmdSecondStage(self,conn,request):
- form = DataForm(node = result.getTag(name='command').getTag(name='x',namespace=NS_DATA))
- sessions[request.getTagAttr('command','sessionid')]['data']['type']=form.getField('calctype')
- sessions[request.getTagAttr('command','sessionid')]['actions']={'cancel':self.cmdCancel,None:self.cmdThirdStage,'previous':cmdFirstStage}
- # The form generation is split out to another method as it may be called by cmdThirdStage
- self.cmdSecondStageReply(conn,request)
-
- def cmdSecondStageReply(self,conn,request):
- reply = request.buildReply('result')
- form = DataForm(title = 'Enter the radius', data=['Enter the radius of the circle (numbers only)',DataField(label='Radius',name='radius',typ='text-single')])
- replypayload = [Node('actions',attrs={'execute':'complete'},payload=[Node('complete'),Node('prev')]),form]
- reply.addChild(name='command',attrs={'xmlns':NS_COMMAND,'node':request.getTagAttr('command','node'),'sessionid':request.getTagAttr('command','sessionid'),'status':'executing'},payload=replypayload)
- self._owner.send(reply)
- raise NodeProcessed
-
- def cmdThirdStage(self,conn,request):
- form = DataForm(node = result.getTag(name='command').getTag(name='x',namespace=NS_DATA))
- try:
- num = float(form.getField('radius'))
- except:
- self.cmdSecondStageReply(conn,request)
- if sessions[request.getTagAttr('command','sessionid')]['data']['type'] == 'circlearea':
- result = num*(pi**2)
- else:
- result = num*2*pi
- reply = result.buildReply(request)
- form = DataForm(typ='result',data=[DataField(label='result',name='result',value=result)])
- reply.addChild(name='command',attrs={'xmlns':NS_COMMAND,'node':request.getTagAttr('command','node'),'sessionid':request.getTagAttr('command','sessionid'),'status':'completed'},payload=form)
- self._owner.send(reply)
- raise NodeProcessed
-
- def cmdCancel(self,conn,request):
- reply = request.buildReply('result')
- reply.addChild(name='command',attrs={'xmlns':NS_COMMAND,'node':request.getTagAttr('command','node'),'sessionid':request.getTagAttr('command','sessionid'),'status':'cancelled'})
- self._owner.send(reply)
- del sessions[request.getTagAttr('command','sessionid')]
-
-
diff --git a/src/common/xmpp/dispatcher.py b/src/common/xmpp/dispatcher.py
deleted file mode 100644
index 95e0f00b0..000000000
--- a/src/common/xmpp/dispatcher.py
+++ /dev/null
@@ -1,383 +0,0 @@
-## transports.py
-##
-## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: dispatcher.py,v 1.40 2006/01/18 19:26:43 normanr Exp $
-
-"""
-Main xmpppy mechanism. Provides library with methods to assign different handlers
-to different XMPP stanzas.
-Contains one tunable attribute: DefaultTimeout (25 seconds by default). It defines time that
-Dispatcher.SendAndWaitForResponce method will wait for reply stanza before giving up.
-"""
-
-import simplexml,time,sys
-from protocol import *
-from client import PlugIn
-
-DefaultTimeout=25
-ID=0
-
-class Dispatcher(PlugIn):
- """ Ancestor of PlugIn class. Handles XMPP stream, i.e. aware of stream headers.
- Can be plugged out/in to restart these headers (used for SASL f.e.). """
- def __init__(self):
- PlugIn.__init__(self)
- DBG_LINE='dispatcher'
- self.handlers={}
- self._expected={}
- self._defaultHandler=None
- self._pendingExceptions=[]
- self._eventHandler=None
- self._cycleHandlers=[]
- self._exported_methods=[self.Process,self.RegisterHandler,self.RegisterDefaultHandler,\
- self.RegisterEventHandler,self.UnregisterCycleHandler,self.RegisterCycleHandler,\
- self.RegisterHandlerOnce,self.UnregisterHandler,self.RegisterProtocol,\
- self.WaitForResponse,self.SendAndWaitForResponse,self.send,self.disconnect,\
- self.SendAndCallForResponse, self.getAnID, ]
-
- def getAnID(self):
- global ID
- ID+=1
- return `ID`
-
- def dumpHandlers(self):
- """ Return set of user-registered callbacks in it's internal format.
- Used within the library to carry user handlers set over Dispatcher replugins. """
- return self.handlers
- def restoreHandlers(self,handlers):
- """ Restores user-registered callbacks structure from dump previously obtained via dumpHandlers.
- Used within the library to carry user handlers set over Dispatcher replugins. """
- self.handlers=handlers
-
- def _init(self):
- """ Registers default namespaces/protocols/handlers. Used internally. """
- self.RegisterNamespace('unknown')
- self.RegisterNamespace(NS_STREAMS)
- self.RegisterNamespace(self._owner.defaultNamespace)
- self.RegisterProtocol('iq',Iq)
- self.RegisterProtocol('presence',Presence)
- self.RegisterProtocol('message',Message)
- self.RegisterDefaultHandler(self.returnStanzaHandler)
- # Register Gajim's event handler as soon as dispatcher begins
- self.RegisterEventHandler(self._owner._caller._event_dispatcher)
-# self.RegisterHandler('error',self.streamErrorHandler,xmlns=NS_STREAMS)
-
- def plugin(self, owner):
- """ Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally."""
- self._init()
- for method in self._old_owners_methods:
- if method.__name__=='send': self._owner_send=method; break
- self._owner.lastErrNode=None
- self._owner.lastErr=None
- self._owner.lastErrCode=None
- self.StreamInit()
-
- def plugout(self):
- """ Prepares instance to be destructed. """
- self.Stream.dispatch=None
- self.Stream.DEBUG=None
- self.Stream.features=None
- self.Stream.destroy()
-
- def StreamInit(self):
- """ Send an initial stream header. """
- self.Stream=simplexml.NodeBuilder()
- self.Stream._dispatch_depth=2
- self.Stream.dispatch=self.dispatch
- self.Stream.stream_header_received=self._check_stream_start
- self._owner.debug_flags.append(simplexml.DBG_NODEBUILDER)
- self.Stream.DEBUG=self._owner.DEBUG
- self.Stream.features=None
- self._metastream=Node('stream:stream')
- self._metastream.setNamespace(self._owner.Namespace)
- self._metastream.setAttr('version','1.0')
- self._metastream.setAttr('xmlns:stream',NS_STREAMS)
- self._metastream.setAttr('to',self._owner.Server)
- self._owner.send("<?xml version='1.0'?>%s>"%str(self._metastream)[:-2])
-
- def _check_stream_start(self,ns,tag,attrs):
- if ns<>NS_STREAMS or tag<>'stream':
- raise ValueError('Incorrect stream start: (%s,%s). Terminating.'%(tag,ns))
-
- def Process(self, timeout=0):
- """ Check incoming stream for data waiting. If "timeout" is positive - block for as max. this time.
- Returns:
- 1) length of processed data if some data were processed;
- 2) '0' string if no data were processed but link is alive;
- 3) 0 (zero) if underlying connection is closed.
- Take note that in case of disconnection detect during Process() call
- disconnect handlers are called automatically.
- """
- for handler in self._cycleHandlers: handler(self)
- if len(self._pendingExceptions) > 0:
- _pendingException = self._pendingExceptions.pop()
- raise _pendingException[0], _pendingException[1], _pendingException[2]
- if self._owner.Connection.pending_data(timeout):
- try: data=self._owner.Connection.receive()
- except IOError: return
- self.Stream.Parse(data)
- if len(self._pendingExceptions) > 0:
- _pendingException = self._pendingExceptions.pop()
- raise _pendingException[0], _pendingException[1], _pendingException[2]
- return len(data)
- return '0' # It means that nothing is received but link is alive.
-
- def RegisterNamespace(self,xmlns,order='info'):
- """ Creates internal structures for newly registered namespace.
- You can register handlers for this namespace afterwards. By default one namespace
- already registered (jabber:client or jabber:component:accept depending on context. """
- self.DEBUG('Registering namespace "%s"'%xmlns,order)
- self.handlers[xmlns]={}
- self.RegisterProtocol('unknown',Protocol,xmlns=xmlns)
- self.RegisterProtocol('default',Protocol,xmlns=xmlns)
-
- def RegisterProtocol(self,tag_name,Proto,xmlns=None,order='info'):
- """ Used to declare some top-level stanza name to dispatcher.
- Needed to start registering handlers for such stanzas.
- Iq, message and presence protocols are registered by default. """
- if not xmlns: xmlns=self._owner.defaultNamespace
- self.DEBUG('Registering protocol "%s" as %s(%s)'%(tag_name,Proto,xmlns), order)
- self.handlers[xmlns][tag_name]={type:Proto, 'default':[]}
-
- def RegisterNamespaceHandler(self,xmlns,handler,typ='',ns='', makefirst=0, system=0):
- """ Register handler for processing all stanzas for specified namespace. """
- self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst, system)
-
- def RegisterHandler(self,name,handler,typ='',ns='',xmlns=None, makefirst=0, system=0):
- """Register user callback as stanzas handler of declared type. Callback must take
- (if chained, see later) arguments: dispatcher instance (for replying), incomed
- return of previous handlers.
- The callback must raise xmpp.NodeProcessed just before return if it want preven
- callbacks to be called with the same stanza as argument _and_, more importantly
- library from returning stanza to sender with error set (to be enabled in 0.2 ve
- Arguments:
- "name" - name of stanza. F.e. "iq".
- "handler" - user callback.
- "typ" - value of stanza's "type" attribute. If not specified any value match
- "ns" - namespace of child that stanza must contain.
- "chained" - chain together output of several handlers.
- "makefirst" - insert handler in the beginning of handlers list instead of
- adding it to the end. Note that more common handlers (i.e. w/o "typ" and "
- will be called first nevertheless.
- "system" - call handler even if NodeProcessed Exception were raised already.
- """
- if not xmlns: xmlns=self._owner.defaultNamespace
- self.DEBUG('Registering handler %s for "%s" type->%s ns->%s(%s)'%(handler,name,typ,ns,xmlns), 'info')
- if not typ and not ns: typ='default'
- if not self.handlers.has_key(xmlns): self.RegisterNamespace(xmlns,'warn')
- if not self.handlers[xmlns].has_key(name): self.RegisterProtocol(name,Protocol,xmlns,'warn')
- if not self.handlers[xmlns][name].has_key(typ+ns): self.handlers[xmlns][name][typ+ns]=[]
- if makefirst: self.handlers[xmlns][name][typ+ns].insert(0,{'func':handler,'system':system})
- else: self.handlers[xmlns][name][typ+ns].append({'func':handler,'system':system})
-
- def RegisterHandlerOnce(self,name,handler,typ='',ns='',xmlns=None,makefirst=0, system=0):
- """ Unregister handler after first call (not implemented yet). """
- if not xmlns: xmlns=self._owner.defaultNamespace
- self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
-
- def UnregisterHandler(self,name,handler,typ='',ns='',xmlns=None):
- """ Unregister handler. "typ" and "ns" must be specified exactly the same as with registering."""
- if not xmlns: xmlns=self._owner.defaultNamespace
- if not typ and not ns: typ='default'
- if not self.handlers[xmlns].has_key(name): return
- if not self.handlers[xmlns][name].has_key(typ+ns): return
- for pack in self.handlers[xmlns][name][typ+ns]:
- if handler==pack['func']: break
- else: pack=None
- try: self.handlers[xmlns][name][typ+ns].remove(pack)
- except ValueError: pass
-
- def RegisterDefaultHandler(self,handler):
- """ Specify the handler that will be used if no NodeProcessed exception were raised.
- This is returnStanzaHandler by default. """
- self._defaultHandler=handler
-
- def RegisterEventHandler(self,handler):
- """ Register handler that will process events. F.e. "FILERECEIVED" event. """
- self._eventHandler=handler
-
- def returnStanzaHandler(self,conn,stanza):
- """ Return stanza back to the sender with <feature-not-implemennted/> error set. """
- if stanza.getType() in ['get','set']:
- conn.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED))
-
- def streamErrorHandler(self,conn,error):
- name,text='error',error.getData()
- for tag in error.getChildren():
- if tag.getNamespace()==NS_XMPP_STREAMS:
- if tag.getName()=='text': text=tag.getData()
- else: name=tag.getName()
- if name in stream_exceptions.keys(): exc=stream_exceptions[name]
- else: exc=StreamError
- raise exc((name,text))
-
- def RegisterCycleHandler(self,handler):
- """ Register handler that will be called on every Dispatcher.Process() call. """
- if handler not in self._cycleHandlers: self._cycleHandlers.append(handler)
-
- def UnregisterCycleHandler(self,handler):
- """ Unregister handler that will is called on every Dispatcher.Process() call."""
- if handler in self._cycleHandlers: self._cycleHandlers.remove(handler)
-
- def Event(self,realm,event,data):
- """ Raise some event. Takes three arguments:
- 1) "realm" - scope of event. Usually a namespace.
- 2) "event" - the event itself. F.e. "SUCESSFULL SEND".
- 3) data that comes along with event. Depends on event."""
- if self._eventHandler: self._eventHandler(realm,event,data)
-
- def dispatch(self,stanza,session=None,direct=0):
- """ Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it.
- Called internally. """
- if not session: session=self
- session.Stream._mini_dom=None
- name=stanza.getName()
-
- if not direct and self._owner._component:
- if name == 'route':
- if stanza.getAttr('error') is None:
- if len(stanza.getChildren()) == 1:
- stanza = stanza.getChildren()[0]
- name=stanza.getName()
- else:
- for each in stanza.getChildren():
- self.dispatch(each,session,direct=1)
- return
- elif name == 'presence':
- return
- elif name in ('features','bind'):
- pass
- else:
- raise UnsupportedStanzaType(name)
-
- if name=='features': session.Stream.features=stanza
-
- xmlns=stanza.getNamespace()
- if not self.handlers.has_key(xmlns):
- self.DEBUG("Unknown namespace: " + xmlns,'warn')
- xmlns='unknown'
- if not self.handlers[xmlns].has_key(name):
- self.DEBUG("Unknown stanza: " + name,'warn')
- name='unknown'
- else:
- self.DEBUG("Got %s/%s stanza"%(xmlns,name), 'ok')
-
- if stanza.__class__.__name__=='Node': stanza=self.handlers[xmlns][name][type](node=stanza)
-
- typ=stanza.getType()
- if not typ: typ=''
- stanza.props=stanza.getProperties()
- ID=stanza.getID()
-
- session.DEBUG("Dispatching %s stanza with type->%s props->%s id->%s"%(name,typ,stanza.props,ID),'ok')
-
- list=['default'] # we will use all handlers:
- if self.handlers[xmlns][name].has_key(typ): list.append(typ) # from very common...
- for prop in stanza.props:
- if self.handlers[xmlns][name].has_key(prop): list.append(prop)
- if typ and self.handlers[xmlns][name].has_key(typ+prop): list.append(typ+prop) # ...to very particular
-
- chain=self.handlers[xmlns]['default']['default']
- for key in list:
- if key: chain = chain + self.handlers[xmlns][name][key]
-
- output=''
- if session._expected.has_key(ID):
- user=0
- if type(session._expected[ID])==type(()):
- cb,args=session._expected[ID]
- session.DEBUG("Expected stanza arrived. Callback %s(%s) found!"%(cb,args),'ok')
- try: cb(session,stanza,**args)
- except Exception, typ:
- if typ.__class__.__name__<>'NodeProcessed': raise
- else:
- session.DEBUG("Expected stanza arrived!",'ok')
- session._expected[ID]=stanza
- else: user=1
- for handler in chain:
- if user or handler['system']:
- try:
- handler['func'](session,stanza)
- except Exception, typ:
- if typ.__class__.__name__<>'NodeProcessed':
- self._pendingExceptions.insert(0, sys.exc_info())
- return
- user=0
- if user and self._defaultHandler: self._defaultHandler(session,stanza)
-
- def WaitForResponse(self, ID, timeout=None):
- """ Block and wait until stanza with specific "id" attribute will come.
- If no such stanza is arrived within timeout, return None.
- If operation failed for some reason then owner's attributes
- lastErrNode, lastErr and lastErrCode are set accordingly. """
- if timeout is None: timeout=DefaultTimeout
- self._expected[ID]=None
- has_timed_out=0
- abort_time=time.time() + timeout
- self.DEBUG("Waiting for ID:%s with timeout %s..." % (ID,timeout),'wait')
- while not self._expected[ID]:
- if not self.Process(0.04):
- self._owner.lastErr="Disconnect"
- return None
- if time.time() > abort_time:
- self._owner.lastErr="Timeout"
- return None
- response=self._expected[ID]
- del self._expected[ID]
- if response.getErrorCode():
- self._owner.lastErrNode=response
- self._owner.lastErr=response.getError()
- self._owner.lastErrCode=response.getErrorCode()
- return response
-
- def SendAndWaitForResponse(self, stanza, timeout=None):
- """ Put stanza on the wire and wait for recipient's response to it. """
- if timeout is None: timeout=DefaultTimeout
- return self.WaitForResponse(self.send(stanza),timeout)
-
- def SendAndCallForResponse(self, stanza, func, args={}):
- """ Put stanza on the wire and call back when recipient replies.
- Additional callback arguments can be specified in args. """
- self._expected[self.send(stanza)]=(func,args)
-
- def send(self,stanza):
- """ Serialise stanza and put it on the wire. Assign an unique ID to it before send.
- Returns assigned ID."""
- if type(stanza) in [type(''), type(u'')]: return self._owner_send(stanza)
- if not isinstance(stanza,Protocol): _ID=None
- elif not stanza.getID():
- global ID
- ID+=1
- _ID=`ID`
- stanza.setID(_ID)
- else: _ID=stanza.getID()
- if self._owner._registered_name and not stanza.getAttr('from'): stanza.setAttr('from',self._owner._registered_name)
- if self._owner._component and stanza.getName()!='bind':
- to=self._owner.Server
- if stanza.getTo() and stanza.getTo().getDomain():
- to=stanza.getTo().getDomain()
- frm=stanza.getFrom()
- if frm.getDomain():
- frm=frm.getDomain()
- route=Protocol('route',to=to,frm=frm,payload=[stanza])
- stanza=route
- stanza.setNamespace(self._owner.Namespace)
- stanza.setParent(self._metastream)
- self._owner_send(stanza)
- return _ID
-
- def disconnect(self):
- """ Send a stream terminator and and handle all incoming stanzas before stream closure. """
- self._owner_send('</stream:stream>')
- while self.Process(1): pass
diff --git a/src/common/xmpp/examples/run_client_bosh.py b/src/common/xmpp/examples/run_client_bosh.py
deleted file mode 100644
index 3ec215a25..000000000
--- a/src/common/xmpp/examples/run_client_bosh.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Example script for usage of BOSHClient class
-# --------------------------------------------
-# run `python run_client_bosh.py` in gajim/src/common/xmpp/examples/ directory
-# and quit with CTRL + c.
-# Script will open TCP connection to Connection Manager, send BOSH initial
-# request and receive initial response. Handling of init response is not
-# done yet.
-
-
-# imports gtk because of gobject.timeout_add() which is used for processing
-# idlequeue
-# TODO: rewrite to thread timer
-import gtk
-import gobject
-import sys, os.path
-
-xmpppy_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
-sys.path.append(xmpppy_dir)
-
-import idlequeue
-from client_bosh import BOSHClient
-
-
-class DummyConnection():
- '''
- Dummy class for test run of bosh_client. I use it because in Connection class
- the gajim.py module is imported and stuff from it is read in there, so it
- would be difficult to debug IMHO. On the other hand, I will have to test with
- Connection class sooner or later somehow because that's the main place where
- BOSHClient should be used.
- DummyConnection class holds and processes IdleQueue for BOSHClient.
- '''
-
- def __init__(self, iq_interval_ms=1000):
- self.classname = self.__class__.__name__
- self.iq_interval_ms = iq_interval_ms
- self.idlequeue = idlequeue.IdleQueue()
- self.timer=gobject.timeout_add(iq_interval_ms, self.process_idlequeue)
-
-
- def process_idlequeue(self):
- '''
- called each iq_interval_ms miliseconds. Checks for idlequeue timeouts.
- '''
- self.idlequeue.process()
- return True
-
- # callback stubs follows
- def _event_dispatcher(self, realm, event, data):
- print "\n>>> %s._event_dispatcher called:" % self.classname
- print ">>> realm: %s, event: %s, data: %s\n" % (realm, event, data)
-
-
- def onconsucc(self):
- print '%s: CONNECTION SUCCEEDED' % self.classname
-
-
- def onconfail(self, retry=None):
- print '%s: CONNECTION FAILED.. retry?: %s' % (self.classname, retry)
-
-
-if __name__ == "__main__":
- dc = DummyConnection()
-
- # you can use my instalation of ejabberd2:
- server = 'star.securitynet.cz'
- bosh_conn_mgr = 'http://star.securitynet.cz/http-bind/'
-
- #server='jabbim.cz'
- #bosh_conn_mgr='http://bind.jabbim.cz/'
-
- bc = BOSHClient(
- server = server,
- bosh_conn_mgr = bosh_conn_mgr,
- bosh_port = 80,
- on_connect = dc.onconsucc,
- on_connect_failure = dc.onconfail,
- caller = dc
- )
-
- bc.set_idlequeue(dc.idlequeue)
-
- bc.connect()
-
- try:
- gtk.main()
- except KeyboardInterrupt:
- dc.process_idlequeue()
-
diff --git a/src/common/xmpp/features.py b/src/common/xmpp/features.py
deleted file mode 100644
index 5b4b6fea6..000000000
--- a/src/common/xmpp/features.py
+++ /dev/null
@@ -1,184 +0,0 @@
-## features.py
-##
-## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $
-
-"""
-This module contains variable stuff that is not worth splitting into separate modules.
-Here is:
- DISCO client and agents-to-DISCO and browse-to-DISCO emulators.
- IBR and password manager.
- jabber:iq:privacy methods
-All these methods takes 'disp' first argument that should be already connected
-(and in most cases already authorised) dispatcher instance.
-"""
-
-from protocol import *
-
-REGISTER_DATA_RECEIVED='REGISTER DATA RECEIVED'
-PRIVACY_LISTS_RECEIVED='PRIVACY LISTS RECEIVED'
-PRIVACY_LIST_RECEIVED='PRIVACY LIST RECEIVED'
-PRIVACY_LISTS_ACTIVE_DEFAULT='PRIVACY LISTS ACTIVE DEFAULT'
-
-### DISCO ### http://jabber.org/protocol/disco ### JEP-0030 ####################
-### Browse ### jabber:iq:browse ### JEP-0030 ###################################
-### Agents ### jabber:iq:agents ### JEP-0030 ###################################
-def _discover(disp,ns,jid,node=None,fb2b=0,fb2a=1):
- """ Try to obtain info from the remote object.
- If remote object doesn't support disco fall back to browse (if fb2b is true)
- and if it doesnt support browse (or fb2b is not true) fall back to agents protocol
- (if gb2a is true). Returns obtained info. Used internally. """
- iq=Iq(to=jid,typ='get',queryNS=ns)
- if node: iq.setQuerynode(node)
- rep=disp.SendAndWaitForResponse(iq)
- if fb2b and not isResultNode(rep): rep=disp.SendAndWaitForResponse(Iq(to=jid,typ='get',queryNS=NS_BROWSE)) # Fallback to browse
- if fb2a and not isResultNode(rep): rep=disp.SendAndWaitForResponse(Iq(to=jid,typ='get',queryNS=NS_AGENTS)) # Fallback to agents
- if isResultNode(rep): return rep.getQueryPayload()
- return []
-
-def discoverItems(disp,jid,node=None):
- """ Query remote object about any items that it contains. Return items list. """
- """ According to JEP-0030:
- query MAY have node attribute
- item: MUST HAVE jid attribute and MAY HAVE name, node, action attributes.
- action attribute of item can be either of remove or update value."""
- ret=[]
- for i in _discover(disp,NS_DISCO_ITEMS,jid,node):
- if i.getName()=='agent' and i.getTag('name'): i.setAttr('name',i.getTagData('name'))
- ret.append(i.attrs)
- return ret
-
-def discoverInfo(disp,jid,node=None):
- """ Query remote object about info that it publishes. Returns identities and features lists."""
- """ According to JEP-0030:
- query MAY have node attribute
- identity: MUST HAVE category and name attributes and MAY HAVE type attribute.
- feature: MUST HAVE var attribute"""
- identities , features = [] , []
- for i in _discover(disp,NS_DISCO_INFO,jid,node):
- if i.getName()=='identity': identities.append(i.attrs)
- elif i.getName()=='feature': features.append(i.getAttr('var'))
- elif i.getName()=='agent':
- if i.getTag('name'): i.setAttr('name',i.getTagData('name'))
- if i.getTag('description'): i.setAttr('name',i.getTagData('description'))
- identities.append(i.attrs)
- if i.getTag('groupchat'): features.append(NS_GROUPCHAT)
- if i.getTag('register'): features.append(NS_REGISTER)
- if i.getTag('search'): features.append(NS_SEARCH)
- return identities , features
-
-### Registration ### jabber:iq:register ### JEP-0077 ###########################
-def getRegInfo(disp,host,info={},sync=True):
- """ Gets registration form from remote host.
- You can pre-fill the info dictionary.
- F.e. if you are requesting info on registering user joey than specify
- info as {'username':'joey'}. See JEP-0077 for details.
- 'disp' must be connected dispatcher instance."""
- iq=Iq('get',NS_REGISTER,to=host)
- for i in info.keys(): iq.setTagData(i,info[i])
- if sync:
- resp=disp.SendAndWaitForResponse(iq)
- _ReceivedRegInfo(disp.Dispatcher,resp, host)
- else: disp.SendAndCallForResponse(iq,_ReceivedRegInfo, {'agent': host})
-
-def _ReceivedRegInfo(con, resp, agent):
- iq=Iq('get',NS_REGISTER,to=agent)
- if not isResultNode(resp): return
- df=resp.getTag('query',namespace=NS_REGISTER).getTag('x',namespace=NS_DATA)
- if df:
- con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,DataForm(node=df),True))
- return
- df=DataForm(typ='form')
- for i in resp.getQueryPayload():
- if type(i)<>type(iq): pass
- elif i.getName()=='instructions': df.addInstructions(i.getData())
- else: df.setField(i.getName()).setValue(i.getData())
- con.Event(NS_REGISTER,REGISTER_DATA_RECEIVED,(agent,df,False))
-
-def register(disp,host,info):
- """ Perform registration on remote server with provided info.
- disp must be connected dispatcher instance.
- Returns true or false depending on registration result.
- If registration fails you can get additional info from the dispatcher's owner
- attributes lastErrNode, lastErr and lastErrCode.
- """
- iq=Iq('set',NS_REGISTER,to=host)
- if type(info)<>type({}): info=info.asDict()
- for i in info.keys(): iq.setTag('query').setTagData(i,info[i])
- resp=disp.SendAndWaitForResponse(iq)
- if isResultNode(resp): return 1
-
-def unregister(disp,host):
- """ Unregisters with host (permanently removes account).
- disp must be connected and authorized dispatcher instance.
- Returns true on success."""
- resp=disp.SendAndWaitForResponse(Iq('set',NS_REGISTER,to=host,payload=[Node('remove')]))
- if isResultNode(resp): return 1
-
-def changePasswordTo(disp,newpassword,host=None):
- """ Changes password on specified or current (if not specified) server.
- disp must be connected and authorized dispatcher instance.
- Returns true on success."""
- if not host: host=disp._owner.Server
- resp=disp.SendAndWaitForResponse(Iq('set',NS_REGISTER,to=host,payload=[Node('username',payload=[disp._owner.Server]),Node('password',payload=[newpassword])]))
- if isResultNode(resp): return 1
-
-### Privacy ### jabber:iq:privacy ### draft-ietf-xmpp-im-19 ####################
-#type=[jid|group|subscription]
-#action=[allow|deny]
-
-def getPrivacyLists(disp):
- """ Requests privacy lists from connected server.
- Returns dictionary of existing lists on success."""
- try:
- dict={'lists':[]}
- resp=disp.SendAndWaitForResponse(Iq('get',NS_PRIVACY))
- if not isResultNode(resp): return
- for list in resp.getQueryPayload():
- if list.getName()=='list': dict['lists'].append(list.getAttr('name'))
- else: dict[list.getName()]=list.getAttr('name')
- return dict
- except: pass
-
-def getPrivacyList(disp,listname):
- """ Requests specific privacy list listname. Returns list of XML nodes (rules)
- taken from the server responce."""
- try:
- resp=disp.SendAndWaitForResponse(Iq('get',NS_PRIVACY,payload=[Node('list',{'name':listname})]))
- if isResultNode(resp): return resp.getQueryPayload()[0]
- except: pass
-
-def setActivePrivacyList(disp,listname=None,typ='active'):
- """ Switches privacy list 'listname' to specified type.
- By default the type is 'active'. Returns true on success."""
- if listname: attrs={'name':listname}
- else: attrs={}
- resp=disp.SendAndWaitForResponse(Iq('set',NS_PRIVACY,payload=[Node(typ,attrs)]))
- if isResultNode(resp): return 1
-
-def setDefaultPrivacyList(disp,listname=None):
- """ Sets the default privacy list as 'listname'. Returns true on success."""
- return setActivePrivacyList(disp,listname,'default')
-
-def setPrivacyList(disp,list):
- """ Set the ruleset. 'list' should be the simpleXML node formatted
- according to RFC 3921 (XMPP-IM) (I.e. Node('list',{'name':listname},payload=[...]) )
- Returns true on success."""
- resp=disp.SendAndWaitForResponse(Iq('set',NS_PRIVACY,payload=[list]))
- if isResultNode(resp): return 1
-
-def delPrivacyList(disp,listname):
- """ Deletes privacy list 'listname'. Returns true on success."""
- resp=disp.SendAndWaitForResponse(Iq('set',NS_PRIVACY,payload=[Node('list',{'name':listname})]))
- if isResultNode(resp): return 1
diff --git a/src/common/xmpp/features_nb.py b/src/common/xmpp/features_nb.py
index 786c9f6bd..dd46c7e4e 100644
--- a/src/common/xmpp/features_nb.py
+++ b/src/common/xmpp/features_nb.py
@@ -15,9 +15,13 @@
# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $
-from features import REGISTER_DATA_RECEIVED, PRIVACY_LISTS_RECEIVED, PRIVACY_LIST_RECEIVED, PRIVACY_LISTS_ACTIVE_DEFAULT
from protocol import *
+REGISTER_DATA_RECEIVED='REGISTER DATA RECEIVED'
+PRIVACY_LISTS_RECEIVED='PRIVACY LISTS RECEIVED'
+PRIVACY_LIST_RECEIVED='PRIVACY LIST RECEIVED'
+PRIVACY_LISTS_ACTIVE_DEFAULT='PRIVACY LISTS ACTIVE DEFAULT'
+
def _on_default_response(disp, iq, cb):
def _on_response(resp):
if isResultNode(resp):
diff --git a/src/common/xmpp/filetransfer.py b/src/common/xmpp/filetransfer.py
deleted file mode 100644
index 87ddc2196..000000000
--- a/src/common/xmpp/filetransfer.py
+++ /dev/null
@@ -1,199 +0,0 @@
-## filetransfer.py
-##
-## Copyright (C) 2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: filetransfer.py,v 1.6 2004/12/25 20:06:59 snakeru Exp $
-
-"""
-This module contains IBB class that is the simple implementation of JEP-0047.
-Note that this is just a transport for data. You have to negotiate data transfer before
-(via StreamInitiation most probably). Unfortunately SI is not implemented yet.
-"""
-
-from protocol import *
-from dispatcher import PlugIn
-import base64
-
-class IBB(PlugIn):
- """ IBB used to transfer small-sized data chunk over estabilished xmpp connection.
- Data is split into small blocks (by default 3000 bytes each), encoded as base 64
- and sent to another entity that compiles these blocks back into the data chunk.
- This is very inefficiend but should work under any circumstances. Note that
- using IBB normally should be the last resort.
- """
- def __init__(self):
- """ Initialise internal variables. """
- PlugIn.__init__(self)
- self.DBG_LINE='ibb'
- self._exported_methods=[self.OpenStream]
- self._streams={}
- self._ampnode=Node(NS_AMP+' amp',payload=[Node('rule',{'condition':'deliver-at','value':'stored','action':'error'}),Node('rule',{'condition':'match-resource','value':'exact','action':'error'})])
-
- def plugin(self,owner):
- """ Register handlers for receiving incoming datastreams. Used internally. """
- self._owner.RegisterHandlerOnce('iq',self.StreamOpenReplyHandler) # Move to StreamOpen and specify stanza id
- self._owner.RegisterHandler('iq',self.IqHandler,ns=NS_IBB)
- self._owner.RegisterHandler('message',self.ReceiveHandler,ns=NS_IBB)
-
- def IqHandler(self,conn,stanza):
- """ Handles streams state change. Used internally. """
- typ=stanza.getType()
- self.DEBUG('IqHandler called typ->%s'%typ,'info')
- if typ=='set' and stanza.getTag('open',namespace=NS_IBB): self.StreamOpenHandler(conn,stanza)
- elif typ=='set' and stanza.getTag('close',namespace=NS_IBB): self.StreamCloseHandler(conn,stanza)
- elif typ=='result': self.StreamCommitHandler(conn,stanza)
- elif typ=='error': self.StreamOpenReplyHandler(conn,stanza)
- else: conn.send(Error(stanza,ERR_BAD_REQUEST))
- raise NodeProcessed
-
- def StreamOpenHandler(self,conn,stanza):
- """ Handles opening of new incoming stream. Used internally. """
- """
-<iq type='set'
- from='romeo@montague.net/orchard'
- to='juliet@capulet.com/balcony'
- id='inband_1'>
- <open sid='mySID'
- block-size='4096'
- xmlns='http://jabber.org/protocol/ibb'/>
-</iq>
-"""
- err=None
- sid,blocksize=stanza.getTagAttr('open','sid'),stanza.getTagAttr('open','block-size')
- self.DEBUG('StreamOpenHandler called sid->%s blocksize->%s'%(sid,blocksize),'info')
- try: blocksize=int(blocksize)
- except: err=ERR_BAD_REQUEST
- if not sid or not blocksize: err=ERR_BAD_REQUEST
- elif sid in self._streams.keys(): err=ERR_UNEXPECTED_REQUEST
- if err: rep=Error(stanza,err)
- else:
- self.DEBUG("Opening stream: id %s, block-size %s"%(sid,blocksize),'info')
- rep=Protocol('iq',stanza.getFrom(),'result',stanza.getTo(),{'id':stanza.getID()})
- self._streams[sid]={'direction':'<'+str(stanza.getFrom()),'block-size':blocksize,'fp':open('/tmp/xmpp_file_'+sid,'w'),'seq':0,'syn_id':stanza.getID()}
- conn.send(rep)
-
- def OpenStream(self,sid,to,fp,blocksize=3000):
- """ Start new stream. You should provide stream id 'sid', the endpoind jid 'to',
- the file object containing info for send 'fp'. Also the desired blocksize can be specified.
- Take into account that recommended stanza size is 4k and IBB uses base64 encoding
- that increases size of data by 1/3."""
- if sid in self._streams.keys(): return
- if not JID(to).getResource(): return
- self._streams[sid]={'direction':'|>'+to,'block-size':blocksize,'fp':fp,'seq':0}
- self._owner.RegisterCycleHandler(self.SendHandler)
- syn=Protocol('iq',to,'set',payload=[Node(NS_IBB+' open',{'sid':sid,'block-size':blocksize})])
- self._owner.send(syn)
- self._streams[sid]['syn_id']=syn.getID()
- return self._streams[sid]
-
- def SendHandler(self,conn):
- """ Send next portion of data if it is time to do it. Used internally. """
- self.DEBUG('SendHandler called','info')
- for sid in self._streams.keys():
- stream=self._streams[sid]
- if stream['direction'][:2]=='|>': cont=1
- elif stream['direction'][0]=='>':
- chunk=stream['fp'].read(stream['block-size'])
- if chunk:
- datanode=Node(NS_IBB+' data',{'sid':sid,'seq':stream['seq']},base64.encodestring(chunk))
- stream['seq']+=1
- if stream['seq']==65536: stream['seq']=0
- conn.send(Protocol('message',stream['direction'][1:],payload=[datanode,self._ampnode]))
- else:
- """ notify the other side about stream closing
- notify the local user about sucessfull send
- delete the local stream"""
- conn.send(Protocol('iq',stream['direction'][1:],'set',payload=[Node(NS_IBB+' close',{'sid':sid})]))
- conn.Event(self.DBG_LINE,'SUCCESSFULL SEND',stream)
- del self._streams[sid]
- self._owner.UnregisterCycleHandler(self.SendHandler)
-
- """
-<message from='romeo@montague.net/orchard' to='juliet@capulet.com/balcony' id='msg1'>
- <data xmlns='http://jabber.org/protocol/ibb' sid='mySID' seq='0'>
- qANQR1DBwU4DX7jmYZnncmUQB/9KuKBddzQH+tZ1ZywKK0yHKnq57kWq+RFtQdCJ
- WpdWpR0uQsuJe7+vh3NWn59/gTc5MDlX8dS9p0ovStmNcyLhxVgmqS8ZKhsblVeu
- IpQ0JgavABqibJolc3BKrVtVV1igKiX/N7Pi8RtY1K18toaMDhdEfhBRzO/XB0+P
- AQhYlRjNacGcslkhXqNjK5Va4tuOAPy2n1Q8UUrHbUd0g+xJ9Bm0G0LZXyvCWyKH
- kuNEHFQiLuCY6Iv0myq6iX6tjuHehZlFSh80b5BVV9tNLwNR5Eqz1klxMhoghJOA
- </data>
- <amp xmlns='http://jabber.org/protocol/amp'>
- <rule condition='deliver-at' value='stored' action='error'/>
- <rule condition='match-resource' value='exact' action='error'/>
- </amp>
-</message>
-"""
-
- def ReceiveHandler(self,conn,stanza):
- """ Receive next portion of incoming datastream and store it write
- it to temporary file. Used internally.
- """
- sid,seq,data=stanza.getTagAttr('data','sid'),stanza.getTagAttr('data','seq'),stanza.getTagData('data')
- self.DEBUG('ReceiveHandler called sid->%s seq->%s'%(sid,seq),'info')
- try: seq=int(seq); data=base64.decodestring(data)
- except: seq=''; data=''
- err=None
- if not sid in self._streams.keys(): err=ERR_ITEM_NOT_FOUND
- else:
- stream=self._streams[sid]
- if not data: err=ERR_BAD_REQUEST
- elif seq<>stream['seq']: err=ERR_UNEXPECTED_REQUEST
- else:
- self.DEBUG('Successfull receive sid->%s %s+%s bytes'%(sid,stream['fp'].tell(),len(data)),'ok')
- stream['seq']+=1
- stream['fp'].write(data)
- if err:
- self.DEBUG('Error on receive: %s'%err,'error')
- conn.send(Error(Iq(to=stanza.getFrom(),frm=stanza.getTo(),payload=[Node(NS_IBB+' close')]),err,reply=0))
-
- def StreamCloseHandler(self,conn,stanza):
- """ Handle stream closure due to all data transmitted.
- Raise xmpppy event specifying successfull data receive. """
- sid=stanza.getTagAttr('close','sid')
- self.DEBUG('StreamCloseHandler called sid->%s'%sid,'info')
- if sid in self._streams.keys():
- conn.send(stanza.buildReply('result'))
- conn.Event(self.DBG_LINE,'SUCCESSFULL RECEIVE',self._streams[sid])
- del self._streams[sid]
- else: conn.send(Error(stanza,ERR_ITEM_NOT_FOUND))
-
- def StreamBrokenHandler(self,conn,stanza):
- """ Handle stream closure due to all some error while receiving data.
- Raise xmpppy event specifying unsuccessfull data receive. """
- syn_id=stanza.getID()
- self.DEBUG('StreamBrokenHandler called syn_id->%s'%syn_id,'info')
- for sid in self._streams.keys():
- stream=self._streams[sid]
- if stream['syn_id']==syn_id:
- if stream['direction'][0]=='<': conn.Event(self.DBG_LINE,'ERROR ON RECEIVE',stream)
- else: conn.Event(self.DBG_LINE,'ERROR ON SEND',stream)
- del self._streams[sid]
-
- def StreamOpenReplyHandler(self,conn,stanza):
- """ Handle remote side reply about is it agree or not to receive our datastream.
- Used internally. Raises xmpppy event specfiying if the data transfer
- is agreed upon."""
- syn_id=stanza.getID()
- self.DEBUG('StreamOpenReplyHandler called syn_id->%s'%syn_id,'info')
- for sid in self._streams.keys():
- stream=self._streams[sid]
- if stream['syn_id']==syn_id:
- if stanza.getType()=='error':
- if stream['direction'][0]=='<': conn.Event(self.DBG_LINE,'ERROR ON RECEIVE',stream)
- else: conn.Event(self.DBG_LINE,'ERROR ON SEND',stream)
- del self._streams[sid]
- elif stanza.getType()=='result':
- if stream['direction'][0]=='|':
- stream['direction']=stream['direction'][1:]
- conn.Event(self.DBG_LINE,'STREAM COMMITTED',stream)
- else: conn.send(Error(stanza,ERR_UNEXPECTED_REQUEST))
diff --git a/src/common/xmpp/roster.py b/src/common/xmpp/roster.py
deleted file mode 100644
index ff64aca5f..000000000
--- a/src/common/xmpp/roster.py
+++ /dev/null
@@ -1,195 +0,0 @@
-## roster.py
-##
-## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: roster.py,v 1.17 2005/05/02 08:38:49 snakeru Exp $
-
-"""
-Simple roster implementation. Can be used though for different tasks like
-mass-renaming of contacts.
-"""
-
-from protocol import *
-from client import PlugIn
-
-class Roster(PlugIn):
- """ Defines a plenty of methods that will allow you to manage roster.
- Also automatically track presences from remote JIDs taking into
- account that every JID can have multiple resources connected. Does not
- currently support 'error' presences.
- You can also use mapping interface for access to the internal representation of
- contacts in roster.
- """
- def __init__(self):
- """ Init internal variables. """
- PlugIn.__init__(self)
- self.DBG_LINE='roster'
- self._data = {}
- self.set=None
- self._exported_methods=[self.getRoster]
-
- def plugin(self,owner,request=1):
- """ Register presence and subscription trackers in the owner's dispatcher.
- Also request roster from server if the 'request' argument is set.
- Used internally."""
- self._owner.RegisterHandler('iq',self.RosterIqHandler,'result',NS_ROSTER,makefirst=1)
- self._owner.RegisterHandler('iq',self.RosterIqHandler,'set',NS_ROSTER)
- self._owner.RegisterHandler('presence',self.PresenceHandler)
- if request: self.Request()
-
- def Request(self,force=0):
- """ Request roster from server if it were not yet requested
- (or if the 'force' argument is set). """
- if self.set is None: self.set=0
- elif not force: return
- self._owner.send(Iq('get',NS_ROSTER))
- self.DEBUG('Roster requested from server','start')
-
- def getRoster(self):
- """ Requests roster from server if neccessary and returns self."""
- if not self.set: self.Request()
- while not self.set: self._owner.Process(10)
- return self
-
- def RosterIqHandler(self,dis,stanza):
- """ Subscription tracker. Used internally for setting items state in
- internal roster representation. """
- sender = stanza.getAttr('from')
- if not sender is None and not sender.bareMatch(
- self._owner.User + '@' + self._owner.Server):
- return
- for item in stanza.getTag('query').getTags('item'):
- jid=item.getAttr('jid')
- if item.getAttr('subscription')=='remove':
- if self._data.has_key(jid): del self._data[jid]
- return
- self.DEBUG('Setting roster item %s...'%jid,'ok')
- if not self._data.has_key(jid): self._data[jid]={}
- self._data[jid]['name']=item.getAttr('name')
- self._data[jid]['ask']=item.getAttr('ask')
- self._data[jid]['subscription']=item.getAttr('subscription')
- self._data[jid]['groups']=[]
- if not self._data[jid].has_key('resources'): self._data[jid]['resources']={}
- for group in item.getTags('group'): self._data[jid]['groups'].append(group.getData())
- self._data[self._owner.User+'@'+self._owner.Server]={'resources':{},'name':None,'ask':None,'subscription':None,'groups':None,}
- self.set=1
-
- def PresenceHandler(self,dis,pres):
- """ Presence tracker. Used internally for setting items' resources state in
- internal roster representation. """
- jid=pres.getFrom()
- if not jid:
- # If no from attribue, it's from server
- jid=self._owner.Server
- jid=JID(jid)
- if not self._data.has_key(jid.getStripped()): self._data[jid.getStripped()]={'name':None,'ask':None,'subscription':'none','groups':['Not in roster'],'resources':{}}
- if type(self._data[jid.getStripped()]['resources'])!=type(dict()):
- self._data[jid.getStripped()]['resources']={}
- item=self._data[jid.getStripped()]
- typ=pres.getType()
-
- if not typ:
- self.DEBUG('Setting roster item %s for resource %s...'%(jid.getStripped(),jid.getResource()),'ok')
- item['resources'][jid.getResource()]=res={'show':None,'status':None,'priority':'0','timestamp':None}
- if pres.getTag('show'): res['show']=pres.getShow()
- if pres.getTag('status'): res['status']=pres.getStatus()
- if pres.getTag('priority'): res['priority']=pres.getPriority()
- if not pres.getTimestamp(): pres.setTimestamp()
- res['timestamp']=pres.getTimestamp()
- elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()]
- # Need to handle type='error' also
-
- def _getItemData(self,jid,dataname):
- """ Return specific jid's representation in internal format. Used internally. """
- jid=jid[:(jid+'/').find('/')]
- return self._data[jid][dataname]
- def _getResourceData(self,jid,dataname):
- """ Return specific jid's resource representation in internal format. Used internally. """
- if jid.find('/')+1:
- jid,resource=jid.split('/',1)
- if self._data[jid]['resources'].has_key(resource): return self._data[jid]['resources'][resource][dataname]
- elif self._data[jid]['resources'].keys():
- lastpri=-129
- for r in self._data[jid]['resources'].keys():
- if int(self._data[jid]['resources'][r]['priority'])>lastpri: resource,lastpri=r,int(self._data[jid]['resources'][r]['priority'])
- return self._data[jid]['resources'][resource][dataname]
- def delItem(self,jid):
- """ Delete contact 'jid' from roster."""
- self._owner.send(Iq('set',NS_ROSTER,payload=[Node('item',{'jid':jid,'subscription':'remove'})]))
- def getAsk(self,jid):
- """ Returns 'ask' value of contact 'jid'."""
- return self._getItemData(jid,'ask')
- def getGroups(self,jid):
- """ Returns groups list that contact 'jid' belongs to."""
- return self._getItemData(jid,'groups')
- def getName(self,jid):
- """ Returns name of contact 'jid'."""
- return self._getItemData(jid,'name')
- def getPriority(self,jid):
- """ Returns priority of contact 'jid'. 'jid' should be a full (not bare) JID."""
- return self._getResourceData(jid,'priority')
- def getRawRoster(self):
- """ Returns roster representation in internal format. """
- return self._data
- def getRawItem(self,jid):
- """ Returns roster item 'jid' representation in internal format. """
- return self._data[jid[:(jid+'/').find('/')]]
- def getShow(self, jid):
- """ Returns 'show' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
- return self._getResourceData(jid,'show')
- def getStatus(self, jid):
- """ Returns 'status' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
- return self._getResourceData(jid,'status')
- def getSubscription(self,jid):
- """ Returns 'subscription' value of contact 'jid'."""
- return self._getItemData(jid,'subscription')
- def getResources(self,jid):
- """ Returns list of connected resources of contact 'jid'."""
- return self._data[jid[:(jid+'/').find('/')]]['resources'].keys()
- def setItem(self,jid,name=None,groups=[]):
- """ Renames contact 'jid' and sets the groups list that it now belongs to."""
- iq=Iq('set',NS_ROSTER)
- query=iq.getTag('query')
- attrs={'jid':jid}
- if name: attrs['name']=name
- item=query.setTag('item',attrs)
- for group in groups: item.addChild(node=Node('group',payload=[group]))
- self._owner.send(iq)
- def getItems(self):
- """ Return list of all [bare] JIDs that the roster is currently tracks."""
- return self._data.keys()
- def keys(self):
- """ Same as getItems. Provided for the sake of dictionary interface."""
- return self._data.keys()
- def __getitem__(self,item):
- """ Get the contact in the internal format. Raises KeyError if JID 'item' is not in roster."""
- return self._data[item]
- def getItem(self,item):
- """ Get the contact in the internal format (or None if JID 'item' is not in roster)."""
- if self._data.has_key(item): return self._data[item]
- def Subscribe(self,jid):
- """ Send subscription request to JID 'jid'."""
- self._owner.send(Presence(jid,'subscribe'))
- def Unsubscribe(self,jid):
- """ Ask for removing our subscription for JID 'jid'."""
- self._owner.send(Presence(jid,'unsubscribe'))
- def Authorize(self,jid):
- """ Authorise JID 'jid'. Works only if these JID requested auth previously. """
- self._owner.send(Presence(jid,'subscribed'))
- def Unauthorize(self,jid):
- """ Unauthorise JID 'jid'. Use for declining authorisation request
- or for removing existing authorization. """
- self._owner.send(Presence(jid,'unsubscribed'))
- def getRaw(self):
- """Returns the internal data representation of the roster."""
- return self._data
diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py
index 307ffd9b1..b843525e7 100644
--- a/src/common/xmpp/roster_nb.py
+++ b/src/common/xmpp/roster_nb.py
@@ -1,8 +1,8 @@
## roster_nb.py
-## based on roster.py
+## based on roster.py
##
## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-## modified by Dimitur Kirov <dkirov@gmail.com>
+## modified by Dimitur Kirov <dkirov@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -21,10 +21,168 @@ Simple roster implementation. Can be used though for different tasks like
mass-renaming of contacts.
'''
-from roster import Roster
-from protocol import NS_ROSTER
+from protocol import *
+from client import PlugIn
+
+class NonBlockingRoster(PlugIn):
+ """ Defines a plenty of methods that will allow you to manage roster.
+ Also automatically track presences from remote JIDs taking into
+ account that every JID can have multiple resources connected. Does not
+ currently support 'error' presences.
+ You can also use mapping interface for access to the internal representation of
+ contacts in roster.
+ """
+ def __init__(self):
+ """ Init internal variables. """
+ PlugIn.__init__(self)
+ self.DBG_LINE='roster'
+ self._data = {}
+ self.set=None
+ self._exported_methods=[self.getRoster]
+
+ def Request(self,force=0):
+ """ Request roster from server if it were not yet requested
+ (or if the 'force' argument is set). """
+ if self.set is None: self.set=0
+ elif not force: return
+ self._owner.send(Iq('get',NS_ROSTER))
+ self.DEBUG('Roster requested from server','start')
+
+ def RosterIqHandler(self,dis,stanza):
+ """ Subscription tracker. Used internally for setting items state in
+ internal roster representation. """
+ sender = stanza.getAttr('from')
+ if not sender is None and not sender.bareMatch(
+ self._owner.User + '@' + self._owner.Server):
+ return
+ for item in stanza.getTag('query').getTags('item'):
+ jid=item.getAttr('jid')
+ if item.getAttr('subscription')=='remove':
+ if self._data.has_key(jid): del self._data[jid]
+ return
+ self.DEBUG('Setting roster item %s...'%jid,'ok')
+ if not self._data.has_key(jid): self._data[jid]={}
+ self._data[jid]['name']=item.getAttr('name')
+ self._data[jid]['ask']=item.getAttr('ask')
+ self._data[jid]['subscription']=item.getAttr('subscription')
+ self._data[jid]['groups']=[]
+ if not self._data[jid].has_key('resources'): self._data[jid]['resources']={}
+ for group in item.getTags('group'): self._data[jid]['groups'].append(group.getData())
+ self._data[self._owner.User+'@'+self._owner.Server]={'resources':{},'name':None,'ask':None,'subscription':None,'groups':None,}
+ self.set=1
+
+ def PresenceHandler(self,dis,pres):
+ """ Presence tracker. Used internally for setting items' resources state in
+ internal roster representation. """
+ jid=pres.getFrom()
+ if not jid:
+ # If no from attribue, it's from server
+ jid=self._owner.Server
+ jid=JID(jid)
+ if not self._data.has_key(jid.getStripped()): self._data[jid.getStripped()]={'name':None,'ask':None,'subscription':'none','groups':['Not in roster'],'resources':{}}
+ if type(self._data[jid.getStripped()]['resources'])!=type(dict()):
+ self._data[jid.getStripped()]['resources']={}
+ item=self._data[jid.getStripped()]
+ typ=pres.getType()
+
+ if not typ:
+ self.DEBUG('Setting roster item %s for resource %s...'%(jid.getStripped(),jid.getResource()),'ok')
+ item['resources'][jid.getResource()]=res={'show':None,'status':None,'priority':'0','timestamp':None}
+ if pres.getTag('show'): res['show']=pres.getShow()
+ if pres.getTag('status'): res['status']=pres.getStatus()
+ if pres.getTag('priority'): res['priority']=pres.getPriority()
+ if not pres.getTimestamp(): pres.setTimestamp()
+ res['timestamp']=pres.getTimestamp()
+ elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()]
+ # Need to handle type='error' also
+
+ def _getItemData(self,jid,dataname):
+ """ Return specific jid's representation in internal format. Used internally. """
+ jid=jid[:(jid+'/').find('/')]
+ return self._data[jid][dataname]
+ def _getResourceData(self,jid,dataname):
+ """ Return specific jid's resource representation in internal format. Used internally. """
+ if jid.find('/')+1:
+ jid,resource=jid.split('/',1)
+ if self._data[jid]['resources'].has_key(resource): return self._data[jid]['resources'][resource][dataname]
+ elif self._data[jid]['resources'].keys():
+ lastpri=-129
+ for r in self._data[jid]['resources'].keys():
+ if int(self._data[jid]['resources'][r]['priority'])>lastpri: resource,lastpri=r,int(self._data[jid]['resources'][r]['priority'])
+ return self._data[jid]['resources'][resource][dataname]
+ def delItem(self,jid):
+ """ Delete contact 'jid' from roster."""
+ self._owner.send(Iq('set',NS_ROSTER,payload=[Node('item',{'jid':jid,'subscription':'remove'})]))
+ def getAsk(self,jid):
+ """ Returns 'ask' value of contact 'jid'."""
+ return self._getItemData(jid,'ask')
+ def getGroups(self,jid):
+ """ Returns groups list that contact 'jid' belongs to."""
+ return self._getItemData(jid,'groups')
+ def getName(self,jid):
+ """ Returns name of contact 'jid'."""
+ return self._getItemData(jid,'name')
+ def getPriority(self,jid):
+ """ Returns priority of contact 'jid'. 'jid' should be a full (not bare) JID."""
+ return self._getResourceData(jid,'priority')
+ def getRawRoster(self):
+ """ Returns roster representation in internal format. """
+ return self._data
+ def getRawItem(self,jid):
+ """ Returns roster item 'jid' representation in internal format. """
+ return self._data[jid[:(jid+'/').find('/')]]
+ def getShow(self, jid):
+ """ Returns 'show' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
+ return self._getResourceData(jid,'show')
+ def getStatus(self, jid):
+ """ Returns 'status' value of contact 'jid'. 'jid' should be a full (not bare) JID."""
+ return self._getResourceData(jid,'status')
+ def getSubscription(self,jid):
+ """ Returns 'subscription' value of contact 'jid'."""
+ return self._getItemData(jid,'subscription')
+ def getResources(self,jid):
+ """ Returns list of connected resources of contact 'jid'."""
+ return self._data[jid[:(jid+'/').find('/')]]['resources'].keys()
+ def setItem(self,jid,name=None,groups=[]):
+ """ Renames contact 'jid' and sets the groups list that it now belongs to."""
+ iq=Iq('set',NS_ROSTER)
+ query=iq.getTag('query')
+ attrs={'jid':jid}
+ if name: attrs['name']=name
+ item=query.setTag('item',attrs)
+ for group in groups: item.addChild(node=Node('group',payload=[group]))
+ self._owner.send(iq)
+ def getItems(self):
+ """ Return list of all [bare] JIDs that the roster is currently tracks."""
+ return self._data.keys()
+ def keys(self):
+ """ Same as getItems. Provided for the sake of dictionary interface."""
+ return self._data.keys()
+ def __getitem__(self,item):
+ """ Get the contact in the internal format. Raises KeyError if JID 'item' is not in roster."""
+ return self._data[item]
+ def getItem(self,item):
+ """ Get the contact in the internal format (or None if JID 'item' is not in roster)."""
+ if self._data.has_key(item): return self._data[item]
+ def Subscribe(self,jid):
+ """ Send subscription request to JID 'jid'."""
+ self._owner.send(Presence(jid,'subscribe'))
+ def Unsubscribe(self,jid):
+ """ Ask for removing our subscription for JID 'jid'."""
+ self._owner.send(Presence(jid,'unsubscribe'))
+ def Authorize(self,jid):
+ """ Authorise JID 'jid'. Works only if these JID requested auth previously. """
+ self._owner.send(Presence(jid,'subscribed'))
+ def Unauthorize(self,jid):
+ """ Unauthorise JID 'jid'. Use for declining authorisation request
+ or for removing existing authorization. """
+ self._owner.send(Presence(jid,'unsubscribed'))
+ def getRaw(self):
+ """Returns the internal data representation of the roster."""
+ return self._data
+ # copypasted methods for roster.py from constructor to here
+
-class NonBlockingRoster(Roster):
def plugin(self, owner, request=1):
''' Register presence and subscription trackers in the owner's dispatcher.
Also request roster from server if the 'request' argument is set.
diff --git a/src/common/xmpp/session.py b/src/common/xmpp/session.py
deleted file mode 100644
index b61e4f6de..000000000
--- a/src/common/xmpp/session.py
+++ /dev/null
@@ -1,350 +0,0 @@
-##
-## XMPP server
-##
-## Copyright (C) 2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-__version__="$Id"
-
-"""
-When your handler is called it is getting the session instance as the first argument.
-This is the difference from xmpppy 0.1 where you got the "Client" instance.
-With Session class you can have "multi-session" client instead of having
-one client for each connection. Is is specifically important when you are
-writing the server.
-"""
-
-from protocol import *
-
-# Transport-level flags
-SOCKET_UNCONNECTED =0
-SOCKET_ALIVE =1
-SOCKET_DEAD =2
-# XML-level flags
-STREAM__NOT_OPENED =1
-STREAM__OPENED =2
-STREAM__CLOSING =3
-STREAM__CLOSED =4
-# XMPP-session flags
-SESSION_NOT_AUTHED =1
-SESSION_AUTHED =2
-SESSION_BOUND =3
-SESSION_OPENED =4
-SESSION_CLOSED =5
-
-class Session:
- """
- The Session class instance is used for storing all session-related info like
- credentials, socket/xml stream/session state flags, roster items (in case of
- client type connection) etc.
- Session object have no means of discovering is any info is ready to be read.
- Instead you should use poll() (recomended) or select() methods for this purpose.
- Session can be one of two types: 'server' and 'client'. 'server' session handles
- inbound connection and 'client' one used to create an outbound one.
- Session instance have multitude of internal attributes. The most imporant is the 'peer' one.
- It is set once the peer is authenticated (client).
- """
- def __init__(self,socket,owner,xmlns=None,peer=None):
- """ When the session is created it's type (client/server) is determined from the beginning.
- socket argument is the pre-created socket-like object.
- It must have the following methods: send, recv, fileno, close.
- owner is the 'master' instance that have Dispatcher plugged into it and generally
- will take care about all session events.
- xmlns is the stream namespace that will be used. Client must set this argument
- If server sets this argument than stream will be dropped if opened with some another namespace.
- peer is the name of peer instance. This is the flag that differentiates client session from
- server session. Client must set it to the name of the server that will be connected, server must
- leave this argument alone.
- """
- self.xmlns=xmlns
- if peer:
- self.TYP='client'
- self.peer=peer
- self._socket_state=SOCKET_UNCONNECTED
- else:
- self.TYP='server'
- self.peer=None
- self._socket_state=SOCKET_ALIVE
- self._sock=socket
- self._send=socket.send
- self._recv=socket.recv
- self.fileno=socket.fileno
- self._registered=0
-
- self.Dispatcher=owner.Dispatcher
- self.DBG_LINE='session'
- self.DEBUG=owner.Dispatcher.DEBUG
- self._expected={}
- self._owner=owner
- if self.TYP=='server': self.ID=`random.random()`[2:]
- else: self.ID=None
-
- self.sendbuffer=''
- self._stream_pos_queued=None
- self._stream_pos_sent=0
- self.deliver_key_queue=[]
- self.deliver_queue_map={}
- self.stanza_queue=[]
-
- self._session_state=SESSION_NOT_AUTHED
- self.waiting_features=[]
- for feature in [NS_TLS,NS_SASL,NS_BIND,NS_SESSION]:
- if feature in owner.features: self.waiting_features.append(feature)
- self.features=[]
- self.feature_in_process=None
- self.slave_session=None
- self.StartStream()
-
- def StartStream(self):
- """ This method is used to initialise the internal xml expat parser
- and to send initial stream header (in case of client connection).
- Should be used after initial connection and after every stream restart."""
- self._stream_state=STREAM__NOT_OPENED
- self.Stream=simplexml.NodeBuilder()
- self.Stream._dispatch_depth=2
- self.Stream.dispatch=self._dispatch
- self.Parse=self.Stream.Parse
- self.Stream.stream_footer_received=self._stream_close
- if self.TYP=='client':
- self.Stream.stream_header_received=self._catch_stream_id
- self._stream_open()
- else:
- self.Stream.stream_header_received=self._stream_open
-
- def receive(self):
- """ Reads all pending incoming data.
- Raises IOError on disconnection.
- Blocks until at least one byte is read."""
- try: received = self._recv(10240)
- except: received = ''
-
- if len(received): # length of 0 means disconnect
- self.DEBUG(`self.fileno()`+' '+received,'got')
- else:
- self.DEBUG('Socket error while receiving data','error')
- self.set_socket_state(SOCKET_DEAD)
- raise IOError("Peer disconnected")
- return received
-
- def sendnow(self,chunk):
- """ Put chunk into "immidiatedly send" queue.
- Should only be used for auth/TLS stuff and like.
- If you just want to shedule regular stanza for delivery use enqueue method.
- """
- if isinstance(chunk,Node): chunk = chunk.__str__().encode('utf-8')
- elif type(chunk)==type(u''): chunk = chunk.encode('utf-8')
- self.enqueue(chunk)
-
- def enqueue(self,stanza):
- """ Takes Protocol instance as argument.
- Puts stanza into "send" fifo queue. Items into the send queue are hold until
- stream authenticated. After that this method is effectively the same as "sendnow" method."""
- if isinstance(stanza,Protocol):
- self.stanza_queue.append(stanza)
- else: self.sendbuffer+=stanza
- if self._socket_state>=SOCKET_ALIVE: self.push_queue()
-
- def push_queue(self,failreason=ERR_RECIPIENT_UNAVAILABLE):
- """ If stream is authenticated than move items from "send" queue to "immidiatedly send" queue.
- Else if the stream is failed then return all queued stanzas with error passed as argument.
- Otherwise do nothing."""
- # If the stream authed - convert stanza_queue into sendbuffer and set the checkpoints
-
- if self._stream_state>=STREAM__CLOSED or self._socket_state>=SOCKET_DEAD: # the stream failed. Return all stanzas that are still waiting for delivery.
- self._owner.deactivatesession(self)
- for key in self.deliver_key_queue: # Not sure. May be I
- self._dispatch(Error(self.deliver_queue_map[key],failreason),trusted=1) # should simply re-dispatch it?
- for stanza in self.stanza_queue: # But such action can invoke
- self._dispatch(Error(stanza,failreason),trusted=1) # Infinite loops in case of S2S connection...
- self.deliver_queue_map,self.deliver_key_queue,self.stanza_queue={},[],[]
- return
- elif self._session_state>=SESSION_AUTHED: # FIXME! äÏÌÖÅÎ ÂÙÔØ ËÁËÏÊ-ÔÏ ÄÒÕÇÏÊ ÆÌÁÇ.
- #### LOCK_QUEUE
- for stanza in self.stanza_queue:
- txt=stanza.__str__().encode('utf-8')
- self.sendbuffer+=txt
- self._stream_pos_queued+=len(txt) # should be re-evaluated for SSL connection.
- self.deliver_queue_map[self._stream_pos_queued]=stanza # position of the stream when stanza will be successfully and fully sent
- self.deliver_key_queue.append(self._stream_pos_queued)
- self.stanza_queue=[]
- #### UNLOCK_QUEUE
-
- def flush_queue(self):
- """ Put the "immidiatedly send" queue content on the wire. Blocks until at least one byte sent."""
- if self.sendbuffer:
- try:
- # LOCK_QUEUE
- sent=self._send(self.sendbuffer) # blocking socket
- except:
- # UNLOCK_QUEUE
- self.set_socket_state(SOCKET_DEAD)
- self.DEBUG("Socket error while sending data",'error')
- return self.terminate_stream()
- self.DEBUG(`self.fileno()`+' '+self.sendbuffer[:sent],'sent')
- self._stream_pos_sent+=sent
- self.sendbuffer=self.sendbuffer[sent:]
- self._stream_pos_delivered=self._stream_pos_sent # Should be acquired from socket somehow. Take SSL into account.
- while self.deliver_key_queue and self._stream_pos_delivered>self.deliver_key_queue[0]:
- del self.deliver_queue_map[self.deliver_key_queue[0]]
- self.deliver_key_queue.remove(self.deliver_key_queue[0])
- # UNLOCK_QUEUE
-
- def _dispatch(self,stanza,trusted=0):
- """ This is callback that is used to pass the received stanza forth to owner's dispatcher
- _if_ the stream is authorised. Otherwise the stanza is just dropped.
- The 'trusted' argument is used to emulate stanza receive.
- This method is used internally.
- """
- self._owner.packets+=1
- print self._owner.packets
- if self._stream_state==STREAM__OPENED or trusted: # if the server really should reject all stanzas after he is closed stream (himeself)?
- self.DEBUG(stanza.__str__(),'dispatch')
- stanza.trusted=trusted
- return self.Dispatcher.dispatch(stanza,self)
-
- def _catch_stream_id(self,ns=None,tag='stream',attrs={}):
- """ This callback is used to detect the stream namespace of incoming stream. Used internally. """
- if not attrs.has_key('id') or not attrs['id']:
- return self.terminate_stream(STREAM_INVALID_XML)
- self.ID=attrs['id']
- if not attrs.has_key('version'): self._owner.Dialback(self)
-
- def _stream_open(self,ns=None,tag='stream',attrs={}):
- """ This callback is used to handle opening stream tag of the incoming stream.
- In the case of client session it just make some validation.
- Server session also sends server headers and if the stream valid the features node.
- Used internally. """
- text='<?xml version="1.0" encoding="utf-8"?>\n<stream:stream'
- if self.TYP=='client':
- text+=' to="%s"'%self.peer
- else:
- text+=' id="%s"'%self.ID
- if not attrs.has_key('to'): text+=' from="%s"'%self._owner.servernames[0]
- else: text+=' from="%s"'%attrs['to']
- if attrs.has_key('xml:lang'): text+=' xml:lang="%s"'%attrs['xml:lang']
- if self.xmlns: xmlns=self.xmlns
- else: xmlns=NS_SERVER
- text+=' xmlns:db="%s" xmlns:stream="%s" xmlns="%s"'%(NS_DIALBACK,NS_STREAMS,xmlns)
- if attrs.has_key('version') or self.TYP=='client': text+=' version="1.0"'
- self.sendnow(text+'>')
- self.set_stream_state(STREAM__OPENED)
- if self.TYP=='client': return
- if tag<>'stream': return self.terminate_stream(STREAM_INVALID_XML)
- if ns<>NS_STREAMS: return self.terminate_stream(STREAM_INVALID_NAMESPACE)
- if self.Stream.xmlns<>self.xmlns: return self.terminate_stream(STREAM_BAD_NAMESPACE_PREFIX)
- if not attrs.has_key('to'): return self.terminate_stream(STREAM_IMPROPER_ADDRESSING)
- if attrs['to'] not in self._owner.servernames: return self.terminate_stream(STREAM_HOST_UNKNOWN)
- self.ourname=attrs['to'].lower()
- if self.TYP=='server' and attrs.has_key('version'):
- # send features
- features=Node('stream:features')
- if NS_TLS in self.waiting_features:
- features.T.starttls.setNamespace(NS_TLS)
- features.T.starttls.T.required
- if NS_SASL in self.waiting_features:
- features.T.mechanisms.setNamespace(NS_SASL)
- for mec in self._owner.SASL.mechanisms:
- features.T.mechanisms.NT.mechanism=mec
- else:
- if NS_BIND in self.waiting_features: features.T.bind.setNamespace(NS_BIND)
- if NS_SESSION in self.waiting_features: features.T.session.setNamespace(NS_SESSION)
- self.sendnow(features)
-
- def feature(self,feature):
- """ Declare some stream feature as activated one. """
- if feature not in self.features: self.features.append(feature)
- self.unfeature(feature)
-
- def unfeature(self,feature):
- """ Declare some feature as illegal. Illegal features can not be used.
- Example: BIND feature becomes illegal after Non-SASL auth. """
- if feature in self.waiting_features: self.waiting_features.remove(feature)
-
- def _stream_close(self,unregister=1):
- """ Write the closing stream tag and destroy the underlaying socket. Used internally. """
- if self._stream_state>=STREAM__CLOSED: return
- self.set_stream_state(STREAM__CLOSING)
- self.sendnow('</stream:stream>')
- self.set_stream_state(STREAM__CLOSED)
- self.push_queue() # decompose queue really since STREAM__CLOSED
- self._owner.flush_queues()
- if unregister: self._owner.unregistersession(self)
- self._destroy_socket()
-
- def terminate_stream(self,error=None,unregister=1):
- """ Notify the peer about stream closure.
- Ensure that xmlstream is not brokes - i.e. if the stream isn't opened yet -
- open it before closure.
- If the error condition is specified than create a stream error and send it along with
- closing stream tag.
- Emulate receiving 'unavailable' type presence just before stream closure.
- """
- if self._stream_state>=STREAM__CLOSING: return
- if self._stream_state<STREAM__OPENED:
- self.set_stream_state(STREAM__CLOSING)
- self._stream_open()
- else:
- self.set_stream_state(STREAM__CLOSING)
- p=Presence(typ='unavailable')
- p.setNamespace(NS_CLIENT)
- self._dispatch(p,trusted=1)
- if error:
- if isinstance(error,Node): self.sendnow(error)
- else: self.sendnow(ErrorNode(error))
- self._stream_close(unregister=unregister)
- if self.slave_session:
- self.slave_session.terminate_stream(STREAM_REMOTE_CONNECTION_FAILED)
-
- def _destroy_socket(self):
- """ Break cyclic dependancies to let python's GC free memory right now."""
- self.Stream.dispatch=None
- self.Stream.stream_footer_received=None
- self.Stream.stream_header_received=None
- self.Stream.destroy()
- self._sock.close()
- self.set_socket_state(SOCKET_DEAD)
-
- def start_feature(self,f):
- """ Declare some feature as "negotiating now" to prevent other features from start negotiating. """
- if self.feature_in_process: raise "Starting feature %s over %s !"%(f,self.feature_in_process)
- self.feature_in_process=f
-
- def stop_feature(self,f):
- """ Declare some feature as "negotiated" to allow other features start negotiating. """
- if self.feature_in_process<>f: raise "Stopping feature %s instead of %s !"%(f,self.feature_in_process)
- self.feature_in_process=None
-
- def set_socket_state(self,newstate):
- """ Change the underlaying socket state.
- Socket starts with SOCKET_UNCONNECTED state
- and then proceeds (possibly) to SOCKET_ALIVE
- and then to SOCKET_DEAD """
- if self._socket_state<newstate: self._socket_state=newstate
-
- def set_session_state(self,newstate):
- """ Change the session state.
- Session starts with SESSION_NOT_AUTHED state
- and then comes through
- SESSION_AUTHED, SESSION_BOUND, SESSION_OPENED and SESSION_CLOSED states.
- """
- if self._session_state<newstate:
- if self._session_state<SESSION_AUTHED and \
- newstate>=SESSION_AUTHED: self._stream_pos_queued=self._stream_pos_sent
- self._session_state=newstate
-
- def set_stream_state(self,newstate):
- """ Change the underlaying XML stream state
- Stream starts with STREAM__NOT_OPENED and then proceeds with
- STREAM__OPENED, STREAM__CLOSING and STREAM__CLOSED states.
- Note that some features (like TLS and SASL)
- requires stream re-start so this state can have non-linear changes. """
- if self._stream_state<newstate: self._stream_state=newstate
diff --git a/src/common/xmpp/transports.py b/src/common/xmpp/transports.py
deleted file mode 100644
index 86c3850fa..000000000
--- a/src/common/xmpp/transports.py
+++ /dev/null
@@ -1,289 +0,0 @@
-## transports.py
-##
-## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov
-##
-## 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; either version 2, or (at your option)
-## any later version.
-##
-## 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.
-
-# $Id: transports.py,v 1.20 2005/05/12 07:35:55 snakeru Exp $
-
-"""
-This module contains the low-level implementations of xmpppy connect methods or
-(in other words) transports for xmpp-stanzas.
-Currently here is three transports:
-direct TCP connect - TCPsocket class
-proxied TCP connect - HTTPPROXYsocket class (CONNECT proxies)
-TLS connection - TLS class. Can be used for SSL connections also.
-
-Transports are stackable so you - f.e. TLS use HTPPROXYsocket or TCPsocket as more low-level transport.
-
-Also exception 'error' is defined to allow capture of this module specific exceptions.
-"""
-
-import socket,select,base64,dispatcher
-from simplexml import ustr
-from client import PlugIn
-from protocol import *
-import sys
-import os
-import errno
-
-DATA_RECEIVED='DATA RECEIVED'
-DATA_SENT='DATA SENT'
-
-def temp_failure_retry(func, *args, **kwargs):
- while True:
- try:
- return func(*args, **kwargs)
- except (os.error, IOError, select.error), ex:
- if hasattr(ex, 'errno'):
- errnum = ex.errno
- elif hasattr(ex, 'args') and ex.args is not None and len(ex.args) > 0:
- errnum = ex.args[0]
- else:
- errnum = -1
- if errnum == errno.EINTR:
- continue
- else:
- raise
-
-class error:
- """An exception to be raised in case of low-level errors in methods of 'transports' module."""
- def __init__(self,comment):
- """Cache the descriptive string"""
- self._comment=comment
-
- def __str__(self):
- """Serialise exception into pre-cached descriptive string."""
- return self._comment
-
-class TCPsocket(PlugIn):
- """ This class defines direct TCP connection method. """
- def __init__(self, server=None, use_srv=True):
- """ Cache connection point 'server'. 'server' is the tuple of (host, port)
- absolutely the same as standard tcp socket uses. """
- PlugIn.__init__(self)
- self.DBG_LINE='socket'
- self._exported_methods=[self.send,self.disconnect]
-
- self._server = server
-
- def plugin(self, owner):
- """ Fire up connection. Return non-empty string on success.
- Also registers self.disconnected method in the owner's dispatcher.
- Called internally. """
- if not self._server: self._server=(self._owner.Server,5222)
- if not self.connect(self._server): return
- self._owner.Connection=self
- self._owner.RegisterDisconnectHandler(self.disconnected)
- return 'ok'
-
- def getHost(self):
- """ Return the 'host' value that is connection is [will be] made to."""
- return self._server[0]
- def getPort(self):
- """ Return the 'port' value that is connection is [will be] made to."""
- return self._server[1]
-
- def connect(self,server=None):
- """ Try to connect. Returns non-empty string on success. """
- try:
- if not server:
- server=self._server
- for ai in socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM):
- try:
- self._sock=socket.socket(*ai[:3])
- self._sock.connect(ai[4])
- self._send=self._sock.sendall
- self._recv=self._sock.recv
- self.DEBUG("Successfully connected to remote host %s"%`server`,'start')
- return 'ok'
- except: continue
- except: pass
-
- def plugout(self):
- """ Disconnect from the remote server and unregister self.disconnected method from
- the owner's dispatcher. """
- self._owner.DeregisterDisconnectHandler(self.disconnected)
- self.shutdown()
- del self._owner.Connection
-
- def receive(self):
- """ Reads all pending incoming data. Calls owner's disconnected() method if appropriate."""
- try: received = self._recv(1024000)
- except: received = ''
-
- while temp_failure_retry(select.select,[self._sock],[],[],0)[0]:
- try: add = self._recv(1024000)
- except: add=''
- received +=add
- if not add: break
-
- if len(received): # length of 0 means disconnect
- self.DEBUG(received,'got')
- if hasattr(self._owner, 'Dispatcher'):
- self._owner.Dispatcher.Event('', DATA_RECEIVED, received)
- else:
- self.DEBUG('Socket error while receiving data','error')
- self._owner.disconnected()
- return received
-
- def send(self,raw_data):
- """ Writes raw outgoing data. Blocks until done.
- If supplied data is unicode string, encodes it to utf-8 before send."""
- if type(raw_data)==type(u''): raw_data = raw_data.encode('utf-8')
- elif type(raw_data)<>type(''): raw_data = ustr(raw_data).encode('utf-8')
- try:
- self._send(raw_data)
- # Avoid printing messages that are empty keepalive packets.
- if raw_data.strip():
- self.DEBUG(raw_data,'sent')
- self._owner.Dispatcher.Event('', DATA_SENT, raw_data)
- except:
- self.DEBUG("Socket error while sending data",'error')
- self._owner.disconnected()
-
- def pending_data(self,timeout=0):
- """ Returns true if there is a data ready to be read. """
- return temp_failure_retry(select.select,[self._sock],[],[],timeout)[0]
-
- def disconnect(self):
- """ Closes the socket. """
- self.DEBUG("Closing socket",'stop')
- self._sock.close()
-
- def disconnected(self):
- """ Called when a Network Error or disconnection occurs.
- Designed to be overidden. """
- self.DEBUG("Socket operation failed",'error')
-
-DBG_CONNECT_PROXY='CONNECTproxy'
-class HTTPPROXYsocket(TCPsocket):
- """ HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class
- redefines only connect method. Allows to use HTTP proxies like squid with
- (optionally) simple authentication (using login and password). """
- def __init__(self,proxy,server,use_srv=True):
- """ Caches proxy and target addresses.
- 'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address)
- and optional keys 'user' and 'password' to use for authentication.
- 'server' argument is a tuple of host and port - just like TCPsocket uses. """
- TCPsocket.__init__(self,server,use_srv)
- self.DBG_LINE=DBG_CONNECT_PROXY
- self._proxy=proxy
-
- def plugin(self, owner):
- """ Starts connection. Used interally. Returns non-empty string on success."""
- owner.debug_flags.append(DBG_CONNECT_PROXY)
- return TCPsocket.plugin(self,owner)
-
- def connect(self,dupe=None):
- """ Starts connection. Connects to proxy, supplies login and password to it
- (if were specified while creating instance). Instructs proxy to make
- connection to the target server. Returns non-empty sting on success. """
- if not TCPsocket.connect(self,(self._proxy['host'],self._proxy['port'])): return
- self.DEBUG("Proxy server contacted, performing authentification",'start')
- connector = ['CONNECT %s:%s HTTP/1.0'%self._server,
- 'Proxy-Connection: Keep-Alive',
- 'Pragma: no-cache',
- 'Host: %s:%s'%self._server,
- 'User-Agent: HTTPPROXYsocket/v0.1']
- if self._proxy.has_key('user') and self._proxy.has_key('password'):
- credentials = '%s:%s'%(self._proxy['user'],self._proxy['password'])
- credentials = base64.encodestring(credentials).strip()
- connector.append('Proxy-Authorization: Basic '+credentials)
- connector.append('\r\n')
- self.send('\r\n'.join(connector))
- try: reply = self.receive().replace('\r','')
- except IOError:
- self.DEBUG('Proxy suddenly disconnected','error')
- self._owner.disconnected()
- return
- try: proto,code,desc=reply.split('\n')[0].split(' ',2)
- except: raise error('Invalid proxy reply')
- if code<>'200':
- self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error')
- self._owner.disconnected()
- return
- while reply.find('\n\n') == -1:
- try: reply += self.receive().replace('\r','')
- except IOError:
- self.DEBUG('Proxy suddenly disconnected','error')
- self._owner.disconnected()
- return
- self.DEBUG("Authentification successfull. Jabber server contacted.",'ok')
- return 'ok'
-
- def DEBUG(self,text,severity):
- """Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy"."""
- return self._owner.DEBUG(DBG_CONNECT_PROXY,text,severity)
-
-class TLS(PlugIn):
- """ TLS connection used to encrypts already estabilished tcp connection."""
- def PlugIn(self,owner,now=0):
- """ If the 'now' argument is true then starts using encryption immidiatedly.
- If 'now' in false then starts encryption as soon as TLS feature is
- declared by the server (if it were already declared - it is ok).
- """
- if owner.__dict__.has_key('TLS'): return # Already enabled.
- PlugIn.PlugIn(self,owner)
- DBG_LINE='TLS'
- if now: return self._startSSL()
- if self._owner.Dispatcher.Stream.features:
- try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
- except NodeProcessed: pass
- else: self._owner.RegisterHandlerOnce('features',self.FeaturesHandler,xmlns=NS_STREAMS)
- self.starttls=None
-
- def plugout(self,now=0):
- """ Unregisters TLS handler's from owner's dispatcher. Take note that encription
- can not be stopped once started. You can only break the connection and start over."""
- self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
-# self._owner.UnregisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS)
-# self._owner.UnregisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS)
-
- def FeaturesHandler(self, conn, feats):
- """ Used to analyse server <features/> tag for TLS support.
- If TLS is supported starts the encryption negotiation. Used internally"""
- if not feats.getTag('starttls',namespace=NS_TLS):
- self.DEBUG("TLS unsupported by remote server.",'warn')
- return
- self.DEBUG("TLS supported by remote server. Requesting TLS start.",'ok')
- self._owner.RegisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS)
- self._owner.RegisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS)
- self._owner.Connection.send('<starttls xmlns="%s"/>'%NS_TLS)
- raise NodeProcessed
-
- def pending_data(self,timeout=0):
- """ Returns true if there possible is a data ready to be read. """
- return self._tcpsock._seen_data or select.select([self._tcpsock._sock],[],[],timeout)[0]
-
- def _startSSL(self):
- """ Immidiatedly switch socket to TLS mode. Used internally."""
- """ Here we should switch pending_data to hint mode."""
- tcpsock=self._owner.Connection
- tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
- tcpsock._sslIssuer = tcpsock._sslObj.issuer()
- tcpsock._sslServer = tcpsock._sslObj.server()
- tcpsock._recv = tcpsock._sslObj.read
- tcpsock._send = tcpsock._sslObj.write
- self.starttls='success'
-
- def StartTLSHandler(self, conn, starttls):
- """ Handle server reply if TLS is allowed to process. Behaves accordingly.
- Used internally."""
- if starttls.getNamespace()<>NS_TLS: return
- self.starttls=starttls.getName()
- if self.starttls=='failure':
- self.DEBUG("Got starttls response: "+self.starttls,'error')
- return
- self.DEBUG("Got starttls proceed response. Switching to TLS/SSL...",'ok')
- self._startSSL()
- self._owner.Dispatcher.PlugOut()
- dispatcher.Dispatcher().PlugIn(self._owner)
diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py
index 2c3364d45..0ae25c24e 100644
--- a/src/common/xmpp/transports_nb.py
+++ b/src/common/xmpp/transports_nb.py
@@ -20,7 +20,6 @@ from simplexml import ustr
from client import PlugIn
from idlequeue import IdleObject
from protocol import *
-from transports import *
import sys
import os
@@ -34,16 +33,19 @@ import logging
log = logging.getLogger('gajim.c.x.transports_nb')
# I don't need to load gajim.py just because of few TLS variables, so I changed
-# :%s/common\.gajim\.DATA_DIR/\'\.\.\/data\'/c
-# :%s/common\.gajim\.MY_CACERTS/\'\%s\/\.gajim\/cacerts\.pem\' \% os\.environ\[\'HOME\'\]/c
+# %s/common\.gajim\.DATA_DIR/\'\.\.\/data\'/c
+# %s/common\.gajim\.MY_CACERTS/\'\%s\/\.gajim\/cacerts\.pem\' \% os\.environ\[\'HOME\'\]/c
# To change it back do:
# %s/\'\.\.\/data\'/common\.gajim\.DATA_DIR/c
-# :%s/\'\%s\/\.gajim\/cacerts\.pem\' \% os\.environ\[\'HOME\'\]/common\.gajim\.MY_CACERTS/c
+# %s/\'%s\/\.gajim\/cacerts\.pem\'\ %\ os\.environ\[\'HOME\'\]/common\.gajim\.MY_CACERTS/c
-# import common.gajim
+import common.gajim
+DATA_RECEIVED='DATA RECEIVED'
+DATA_SENT='DATA SENT'
+
USE_PYOPENSSL = False
try:
@@ -771,16 +773,16 @@ class NonBlockingTLS(PlugIn):
#tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
tcpsock.ssl_errnum = 0
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback)
- cacerts = os.path.join('../data', 'other', 'cacerts.pem')
+ cacerts = os.path.join(common.gajim.DATA_DIR, 'other', 'cacerts.pem')
try:
tcpsock._sslContext.load_verify_locations(cacerts)
except:
log.warning('Unable to load SSL certificats from file %s' % \
os.path.abspath(cacerts))
# load users certs
- if os.path.isfile('%s/.gajim/cacerts.pem' % os.environ['HOME']):
+ if os.path.isfile(common.gajim.MY_CACERTS):
store = tcpsock._sslContext.get_cert_store()
- f = open('%s/.gajim/cacerts.pem' % os.environ['HOME'])
+ f = open(common.gajim.MY_CACERTS)
lines = f.readlines()
i = 0
begin = -1
@@ -795,11 +797,11 @@ class NonBlockingTLS(PlugIn):
store.add_cert(X509cert)
except OpenSSL.crypto.Error, exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\
- ('%s/.gajim/cacerts.pem' % os.environ['HOME'], exception_obj.args[0][0][2]))
+ (common.gajim.MY_CACERTS, exception_obj.args[0][0][2]))
except:
log.warning(
'Unknown error while loading certificate from file %s' % \
- '%s/.gajim/cacerts.pem' % os.environ['HOME'])
+ common.gajim.MY_CACERTS)
begin = -1
i += 1
tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock)
diff --git a/src/config.py b/src/config.py
index fd37bca5f..4e4c49161 100644
--- a/src/config.py
+++ b/src/config.py
@@ -515,11 +515,11 @@ class PreferencesWindow:
def on_sort_by_show_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'sort_by_show')
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
def on_show_avatars_in_roster_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_avatars_in_roster')
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
# Redraw connected groupchats (in an ugly way)
for account in gajim.connections:
if gajim.connections[account].connected:
@@ -530,14 +530,14 @@ class PreferencesWindow:
def on_show_status_msgs_in_roster_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'show_status_msgs_in_roster')
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
for ctl in gajim.interface.msg_win_mgr.controls():
if ctl.type_id == message_control.TYPE_GC:
ctl.update_ui()
def on_sort_by_show_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'sort_by_show')
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
def on_emoticons_combobox_changed(self, widget):
active = widget.get_active()
@@ -570,7 +570,7 @@ class PreferencesWindow:
ctl.chat_buttons_set_visible(active)
gajim.config.set('compact_view', active)
gajim.interface.save_config()
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
def on_xhtml_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
@@ -1417,7 +1417,6 @@ class AccountsWindow:
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()
@@ -1752,8 +1751,10 @@ class AccountsWindow:
gajim.config.del_per('accounts', old_name)
if self.current_account == old_name:
self.current_account = new_name
+ if old_name == gajim.ZEROCONF_ACC_NAME:
+ gajim.ZEROCONF_ACC_NAME = new_name
# refresh roster
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
self.init_accounts()
self.select_account(new_name)
@@ -2077,7 +2078,7 @@ class AccountsWindow:
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
def on_enable_zeroconf_checkbutton2_toggled(self, widget):
# don't do anything if there is an account with the local name but is a
@@ -2119,7 +2120,7 @@ class AccountsWindow:
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
elif not gajim.config.get('enable_zeroconf') and widget.get_active():
@@ -2156,7 +2157,7 @@ class AccountsWindow:
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.interface.save_config()
@@ -2587,7 +2588,7 @@ class RemoveAccountWindow:
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
if gajim.interface.instances.has_key('accounts'):
gajim.interface.instances['accounts'].init_accounts()
@@ -3421,7 +3422,7 @@ class AccountCreationWizardWindow:
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
- gajim.interface.roster.draw_roster()
+ gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.interface.save_config()
diff --git a/src/conversation_textview.py b/src/conversation_textview.py
index 9db4f4d1e..2b6cf7fcf 100644
--- a/src/conversation_textview.py
+++ b/src/conversation_textview.py
@@ -854,15 +854,8 @@ class ConversationTextview:
exitcode = p.wait()
if exitcode == 0:
- convert_version = helpers.get_output_of_command(
- 'convert -version')[0].split()[2]
- convert_version = [int(n) for n in convert_version.split('.')]
- if convert_version > [6, 3, 4]:
- # -alpha option was added in 6.3.5 release
- alpha = ['-alpha', 'off']
- else:
- alpha = []
- p = Popen(['convert'] + alpha + [tmpfile + '.ps', tmpfile + '.png'],
+ latex_png_dpi = gajim.config.get('latex_png_dpi')
+ p = Popen(['convert', '-background', 'white', '-flatten', '-density', latex_png_dpi, tmpfile + '.ps', tmpfile + '.png'],
cwd=gettempdir())
exitcode = p.wait()
diff --git a/src/features_window.py b/src/features_window.py
index 42acc823c..5c8f10290 100644
--- a/src/features_window.py
+++ b/src/features_window.py
@@ -99,10 +99,6 @@ class FeaturesWindow:
_('Encrypting chatmessages.'),
_('Requires python-crypto.'),
_('Requires python-crypto.')),
- _('Off the Record Encryption'): (self.otr_available,
- _('Encrypting chatmessages in a way that even works through gateways.'),
- _('Requires pyotr and libotr (see http://trac.gajim.org/wiki/OTR).'),
- _('Requires pyotr and libotr (see http://trac.gajim.org/wiki/OTR).')),
_('RST Generator'): (self.docutils_available,
_('Generate XHTML output from RST code (see http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html).'),
_('Requires python-docutils.'),
@@ -312,11 +308,6 @@ class FeaturesWindow:
from common import gajim
return gajim.HAVE_PYCRYPTO
- def otr_available(self):
- if gajim.otr_module:
- return True
- return False
-
def docutils_available(self):
try:
import docutils
diff --git a/src/gajim.py b/src/gajim.py
index 234ab6214..47d90a8d2 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -142,8 +142,19 @@ try:
import gtk
except Warning, msg:
if str(msg) == 'could not open display':
- print >> sys.stderr, _('Gajim needs X server to run. Quiting...')
- sys.exit()
+ if sys.platform == 'darwin':
+ # It seems there is no way to open X11 without also
+ # opening an xterm. Even Apple's open-x11 script
+ # opens the application AND an xterm.
+ os.system('/Applications/Utilities/X11.app/Contents/MacOS/X11 &')
+ try:
+ import gtk
+ except Warning, msg:
+ print >> sys.stderr, _('No X11 running and failed to start it! Quitting...')
+ sys.exit()
+ else:
+ print >> sys.stderr, _('Gajim needs X server to run. Quiting...')
+ sys.exit()
warnings.resetwarnings()
if os.name == 'nt':
@@ -255,214 +266,6 @@ from common import helpers
from common import optparser
from common import dataforms
-from common.xmpp import Message as XmppMessage
-
-try:
- import otr, otr_windows
- gajim.otr_module = otr
- gajim.otr_windows = otr_windows
-except ImportError:
- gajim.otr_module = None
- gajim.otr_windows = None
-
-def add_appdata(data, context):
- account = data
- context.app_data = otr_windows.ContactOtrSMPWindow(
- unicode(context.username), account)
-
-gajim.otr_add_appdata = add_appdata
-
-def otr_dialog_destroy(widget, *args, **kwargs):
- widget.destroy()
-
-class OtrlMessageAppOps:
- def gajim_log(self, msg, account, fjid, no_print=False):
- if not isinstance(fjid, unicode):
- fjid = unicode(fjid)
- if not isinstance(account, unicode):
- account = unicode(account)
- resource=gajim.get_resource_from_jid(fjid)
- tim = time.localtime()
-
- if not no_print:
- ctrl = self.get_control(fjid, account)
- if ctrl:
- ctrl.print_conversation_line(u'[OTR] %s' % \
- msg, 'status', '', None)
- id = gajim.logger.write('chat_msg_recv', fjid,
- message='[OTR: %s]' % msg, tim=tim)
- # gajim.logger.write() only marks a message as unread
- # (and so only returns an id) when fjid is a real contact
- # (NOT if it's a GC private chat)
- if id:
- gajim.logger.set_read_messages([id])
-
- def get_control(self, fjid, account):
- # first try to get the window with the full jid
- ctrls = gajim.interface.msg_win_mgr.get_chat_controls(fjid, account)
- if ctrls:
- # got one, be happy
- return ctrls[0]
-
- # otherwise try without the resource
- ctrls = gajim.interface.msg_win_mgr.get_chat_controls(
- gajim.get_jid_without_resource(fjid), account)
- # but only use it when it is not a GC window
- if ctrls and ctrls[0].TYPE_ID == message_control.TYPE_CHAT:
- return ctrls[0]
-
- def policy(self, opdata=None, context=None):
- policy = gajim.config.get_per('contacts', context.username,
- "otr_flags")
- if policy <= 0:
- policy = gajim.config.get_per('contacts',
- gajim.get_jid_without_resource(
- context.username), 'otr_flags')
- if policy <= 0:
- policy = gajim.config.get_per('accounts',
- opdata['account'], 'otr_flags')
- return policy
-
- def create_privkey(self, opdata='', accountname='', protocol=''):
- dialog = gtk.Dialog(
- title = _('Generating...'),
- parent = gajim.interface.roster.window,
- flags = gtk.DIALOG_MODAL,
- buttons = (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
- permlabel = gtk.Label(_('Generating a private key for %s...') \
- % accountname)
- permlabel.set_padding(20, 20)
- dialog.set_response_sensitive(gtk.RESPONSE_CLOSE, False)
- dialog.connect('destroy', otr_dialog_destroy)
- dialog.connect('response', otr_dialog_destroy)
- dialog.vbox.pack_start(permlabel)
- dialog.get_root_window().raise_()
- dialog.show_all()
- dialog.map()
- for c in dialog.get_children():
- c.show_now()
- c.map()
-
- while gtk.events_pending():
- gtk.main_iteration(block = False)
-
- otr.otrl_privkey_generate(
- gajim.connections[opdata['account']].otr_userstates,
- os.path.join(gajimpaths.root,
- '%s.key' % opdata['account']).encode(),
- accountname, gajim.OTR_PROTO)
- permlabel.set_text(_('Generating a private key for %s...\n' \
- 'done.') % accountname)
- dialog.set_response_sensitive(gtk.RESPONSE_CLOSE, True)
-
- def is_logged_in(self, opdata={}, accountname='', protocol='',
- recipient=""):
- contact = gajim.contacts.get_contact_from_full_jid(
- opdata['account'], recipient)
- if contact:
- return contact.show \
- in ['dnd', 'xa', 'chat', 'online', 'away',
- 'invisible']
- return 0
-
- def inject_message(self, opdata=None, accountname='', protocol='',
- recipient='', message=''):
- msg_type = otr.otrl_proto_message_type(message)
-
- if 'kwargs' not in opdata or 'urgent' in opdata:
- # don't use send_message here to have the message
- # sent immediatly. This results in being able to
- # disconnect from OTR sessions before quitting
- stanza = XmppMessage(to = recipient,
- body = message, typ='chat')
- gajim.connections[opdata['account']].connection. \
- send(stanza, now = True)
- return
-
- if msg_type == otr.OTRL_MSGTYPE_QUERY:
- # split away XHTML-contaminated explanatory message
- message = unicode(message.splitlines()[0])
- message += _(u'\nThis user has requested an ' \
- 'Off-the-Record private conversation. ' \
- 'However, you do not have a plugin to ' \
- 'support that.\n' \
- 'See http://otr.cypherpunks.ca/ for more ' \
- 'information.')
-
- gajim.connections[opdata['account']].connection.send(
- common.xmpp.Message(to = recipient,
- body = message, typ = 'chat'))
- return
-
- gajim.connections[opdata['account']].send_message(recipient,
- message, **opdata['kwargs'])
-
- def notify(sef, opdata=None, username='', **kwargs):
- self.gajim_log('Notify: ' + str(kwargs), opdata['account'],
- username)
-
- def display_otr_message(self, opdata=None, username="", msg="", **kwargs):
- self.gajim_log('OTR Message: ' + msg, opdata['account'],
- username)
- return 0
-
- def update_context_list(self, **kwargs):
- # FIXME stub FIXME #
- pass
-
- def protocol_name(self, opdata=None, protocol=""):
- return 'XMPP'
-
- def new_fingerprint(self, opdata=None, username='', fingerprint='',
- **kwargs):
- self.gajim_log('New fingerprint for %s: %s' % (username,
- otr.otrl_privkey_hash_to_human(fingerprint)),
- opdata['account'], username)
-
- def write_fingerprints(self, opdata=''):
- otr.otrl_privkey_write_fingerprints(
- gajim.connections[opdata['account']].otr_userstates,
- os.path.join(gajimpaths.root, '%s.fpr' % \
- opdata['account']).encode())
-
- def gone_secure(self, opdata='', context=None):
- trust = context.active_fingerprint.trust \
- and 'verified' or 'unverified'
- self.gajim_log('%s secured OTR connection started' % trust,
- opdata['account'], context.username, no_print = True)
-
- ctrl = self.get_control(context.username, opdata['account'])
- if ctrl:
- ctrl.update_otr(True)
-
- def gone_insecure(self, opdata='', context=None):
- self.gajim_log('Private conversation with %s lost.',
- opdata['account'], context.username)
-
- ctrl = self.get_control(context.username, opdata['account'])
- if ctrl:
- ctrl.update_otr(True)
-
- def still_secure(self, opdata=None, context=None, is_reply=0):
- ctrl = self.get_control(context.username, opdata['account'])
- if ctrl:
- ctrl.update_otr(True)
-
- self.gajim_log('OTR connection was refreshed',
- opdata['account'], context.username)
-
- def log_message(self, opdata=None, message=''):
- gajim.log.debug(message)
-
- def max_message_size(self, **kwargs):
- return 0
-
- def account_name(self, opdata=None, account='', protocol=''):
- return gajim.get_name_from_jid(opdata['account'],
- unicode(account))
-
-gajim.otr_ui_ops = OtrlMessageAppOps()
-
if verbose: gajim.verbose = True
del verbose
@@ -600,8 +403,11 @@ def on_exit():
if os.path.exists(pid_filename):
os.remove(pid_filename)
if sys.platform == 'darwin':
- import osx
- osx.shutdown()
+ try:
+ import osx
+ osx.shutdown()
+ except ImportError:
+ pass
import atexit
atexit.register(on_exit)
@@ -858,7 +664,7 @@ class Interface:
if resource == gajim.connections[account].server_resource:
return
contact1 = gajim.contacts.create_contact(jid = ji,
- name = gajim.nicks[account], groups = [],
+ name = gajim.nicks[account], groups = ['self_contact'],
show = array[1], status = status_message, sub = 'both',
ask = 'none', priority = priority, keyID = keyID,
resource = resource)
@@ -1648,18 +1454,26 @@ class Interface:
gmail_new_messages, gmail_new_messages)
if gajim.config.get('notify_on_new_gmail_email_extra'):
+ cnt = 0
for gmessage in gmail_messages_list:
- #FIXME: emulate Gtalk client popups. find out what they parse and how
- #they decide what to show
- # each message has a 'From', 'Subject' and 'Snippet' field
- text += _('\nFrom: %(from_address)s') % \
- {'from_address': gmessage['From']}
+ #FIXME: emulate Gtalk client popups. find out what they parse and
+ # how they decide what to show each message has a 'From',
+ # 'Subject' and 'Snippet' field
+ if cnt >=5:
+ break
+ senders = reduce(lambda b, a: a + ',\n ' + b,
+ gmessage['From'])
+ text += _('\n\nFrom: %(from_address)s\nSubject: %(subject)s\n%(snippet)s') % \
+ {'from_address': senders, 'subject': gmessage['Subject'],
+ 'snippet': gmessage['Snippet']}
+ cnt += 1
if gajim.config.get_per('soundevents', 'gmail_received', 'enabled'):
helpers.play_sound('gmail_received')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.popup(_('New E-mail'), jid, account, 'gmail',
- path_to_image = path, title = title, text = text)
+ path_to_image=path, title=title,
+ text=gobject.markup_escape_text(text))
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewGmail', (account, array))
@@ -2041,7 +1855,7 @@ class Interface:
connection = gajim.connections[account]
contact = gajim.contacts.create_contact(jid = jid.getStripped(),
resource = resource, show = connection.get_status())
- self.new_chat(session, contact, account, resource = resource)
+ self.new_chat(contact, account, resource = resource, session = session)
negotiation.FeatureNegotiationWindow(account, jid, session, form)
@@ -2446,7 +2260,7 @@ class Interface:
if not session:
session = gajim.connections[account].get_or_create_session(fjid, None)
- self.new_chat(session, contact, account, resource = resource)
+ self.new_chat(contact, account, resource = resource, session = session)
ctrl = session.control
gajim.last_message_time[account][jid] = 0 # long time ago
@@ -2514,7 +2328,6 @@ class Interface:
self.roster.draw_contact(jid, account)
if w:
w.set_active_tab(ctrl)
- w.window.present()
w.window.window.focus()
# Using isinstance here because we want to catch all derived types
if isinstance(ctrl, ChatControlBase):
@@ -2750,11 +2563,12 @@ class Interface:
def join_gc_room(self, account, room_jid, nick, password, minimize=False,
is_continued=False):
'''joins the room immediately'''
+ if not nick:
+ nick = gajim.nicks[account]
if self.msg_win_mgr.has_window(room_jid, account) and \
gajim.gc_connected[account][room_jid]:
gc_ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
win = gc_ctrl.parent_win
- win.window.present()
win.set_active_tab(gc_ctrl)
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
return
@@ -2782,7 +2596,6 @@ class Interface:
if not minimized_control_exists:
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
gc_control.parent_win.set_active_tab(gc_control)
- gc_control.parent_win.window.present()
gajim.connections[account].join_gc(nick, room_jid, password)
if password:
gajim.gc_passwords[room_jid] = password
@@ -2841,7 +2654,7 @@ class Interface:
# We call this here to avoid race conditions with widget validation
session.control.read_queue()
- def new_chat(self, session, contact, account, resource = None):
+ def new_chat(self, contact, account, resource = None, session = None):
# Get target window, create a control, and associate it with the window
type_ = message_control.TYPE_CHAT
@@ -2875,14 +2688,13 @@ class Interface:
session = gajim.connections[account].get_or_create_session(fjid, None)
if not self.msg_win_mgr.has_window(fjid, account):
- session.control = self.new_chat(session, contact, account,
- resource=resource)
+ session.control = self.new_chat(contact, account,
+ resource=resource, session=session)
if len(gajim.events.get_events(account, fjid)):
session.control.read_queue()
mw = session.control.parent_win
mw.set_active_tab(session.control)
- mw.window.present()
# For JEP-0172
if added_to_roster:
session.control.user_nick = gajim.nicks[account]
@@ -2896,46 +2708,31 @@ class Interface:
if resource:
fjid += '/' + resource
- conn = gajim.connections[account]
+ ctrl = None
- if not session and fjid in conn.sessions:
- sessions = filter(lambda s: isinstance(s, ChatControlSession),
- conn.sessions[fjid].values())
-
- # look for an existing session with a chat control
- for s in sessions:
- if s.control:
- session = s
- break
-
- if not session and not len(sessions) == 0:
- # there are no sessions with chat controls, just take the first one
- session = sessions[0]
-
- if not session:
- # couldn't find an existing ChatControlSession, just make a new one
- session = conn.make_new_session(fjid, None, 'chat')
-
- if not session.control:
- # open a new chat control
- session.control = self.new_chat(session, contact, account,
- resource=resource)
+ if session:
+ ctrl = session.control
+ else:
+ win = self.msg_win_mgr.get_window(fjid, account)
- if len(gajim.events.get_events(account, fjid)):
- session.control.read_queue()
+ if win:
+ ctrl = win.get_controls(fjid, account)[0]
+ if not ctrl:
+ ctrl = self.new_chat(contact, account,
+ resource = resource, session = session)
# last message is long time ago
- gajim.last_message_time[account][session.control.get_full_jid()] = 0
+ gajim.last_message_time[account][ctrl.get_full_jid()] = 0
+
+ win = ctrl.parent_win
- win = session.control.parent_win
- win.set_active_tab(session.control)
+ win.set_active_tab(ctrl)
- if conn.is_zeroconf and conn.status in ('offline', 'invisible'):
+ if gajim.connections[account].is_zeroconf and \
+ gajim.connections[account].status in ('offline', 'invisible'):
for ctrl in win.get_controls(fjid, account):
ctrl.got_disconnected()
- win.window.present()
-
################################################################################
### Other Methods
################################################################################
@@ -3222,6 +3019,10 @@ class Interface:
if os.name != 'nt' and gajim.config.get('check_if_gajim_is_default'):
gtkgui_helpers.possibly_set_gajim_as_xmpp_handler()
+ for account in gajim.config.get_per('accounts'):
+ if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+ gajim.ZEROCONF_ACC_NAME = account
+ break
# Is gnome configured to activate row on single click ?
try:
import gconf
@@ -3466,8 +3267,11 @@ if __name__ == '__main__':
check_paths.check_and_possibly_create_paths()
if sys.platform == 'darwin':
- import osx
- osx.init()
+ try:
+ import osx
+ osx.init()
+ except ImportError:
+ pass
Interface()
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index 84b31fa0a..cf8e3bc2d 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -916,6 +916,8 @@ class GroupchatControl(ChatControlBase):
gajim.contacts.remove_gc_contact(self.account, gc_contact)
gajim.gc_connected[self.account][self.room_jid] = False
ChatControlBase.got_disconnected(self)
+ # Tell connection to note the date we disconnect to avoid duplicate logs
+ gajim.connections[self.account].gc_got_disconnected(self.room_jid)
# We don't redraw the whole banner here, because only icon change
self._update_banner_state_image()
if self.parent_win:
@@ -1638,12 +1640,13 @@ class GroupchatControl(ChatControlBase):
def shutdown(self, status='offline'):
# destroy banner tooltip - bug #pygtk for that!
self.subject_tooltip.destroy()
+ if gajim.gc_connected[self.account][self.room_jid]:
+ # Tell connection to note the date we disconnect to avoid duplicate
+ # logs. We do it only when connected because if connection was lost
+ # there may be new messages since disconnection.
+ gajim.connections[self.account].gc_got_disconnected(self.room_jid)
gajim.connections[self.account].send_gc_status(self.nick, self.room_jid,
show='offline', status=status)
- # save in fast table in DB at what time we had last message
- last_history_time = \
- gajim.connections[self.account].last_history_time[self.room_jid]
- gajim.logger.set_room_last_message_time(self.room_jid, last_history_time)
nick_list = gajim.contacts.get_nick_list(self.account, self.room_jid)
for nick in nick_list:
# Update pm chat window
@@ -2073,7 +2076,6 @@ class GroupchatControl(ChatControlBase):
ctrl = win.get_controls(nick_jid, self.account)[0]
win.set_active_tab(ctrl)
- win.window.present()
return ctrl
diff --git a/src/history_window.py b/src/history_window.py
index 062adba9d..6c87d0940 100644
--- a/src/history_window.py
+++ b/src/history_window.py
@@ -65,8 +65,6 @@ class HistoryWindow:
'''Class for browsing logs of conversations with contacts'''
def __init__(self, jid = None, account = None):
- self.mark_days_idle_call_id = None
-
xml = gtkgui_helpers.get_glade('history_window.glade')
self.window = xml.get_widget('history_window')
self.jid_entry = xml.get_widget('jid_entry')
@@ -117,22 +115,25 @@ class HistoryWindow:
self.completion_dict = {}
self.accounts_seen_online = [] # Update dict when new accounts connect
self.jids_to_search = []
- self._fill_completion_dict()
+
+ # This will load history too
+ gobject.idle_add(self._fill_completion_dict().next)
if jid:
self.jid_entry.set_text(jid)
xml.signal_autoconnect(self)
- self._load_history(jid, account)
self.window.show_all()
def _fill_completion_dict(self):
- '''Fill completion_dict for key auto completion.
+ '''Fill completion_dict for key auto completion. Then load history for
+ current jid (by calling another function).
Key will be either jid or full_completion_name
(contact name or long description like "pm-contact from groupchat....")
{key : (jid, account, nick_name, full_completion_name}
+ this is a generator and does pseudo-threading via idle_add()
'''
liststore = gtkgui_helpers.get_completion_liststore(self.jid_entry)
@@ -150,8 +151,14 @@ class HistoryWindow:
muc_active_img = gtkgui_helpers.load_icon('muc_active')
contact_img = gajim.interface.jabber_state_images['16']['online']
muc_active_pix = muc_active_img.get_pixbuf()
- contact_pix = contact_img.get_pixbuf()
+ contact_pix = contact_img.get_pixbuf()
+
keys = self.completion_dict.keys()
+ # Move the actual jid at first so we load history faster
+ actual_jid = self.jid_entry.get_text().decode('utf-8')
+ if actual_jid in keys:
+ keys.remove(actual_jid)
+ keys.insert(0, actual_jid)
# Map jid to info tuple
# Warning : This for is time critical with big DB
for key in keys:
@@ -180,13 +187,17 @@ class HistoryWindow:
info_name = nick
else:
pix = contact_pix
-
+
liststore.append((pix, completed))
self.completion_dict[key] = (info_jid, info_acc, info_name,
info_completion)
self.completion_dict[completed] = (info_jid, info_acc,
info_name, info_completion)
+ if key == actual_jid:
+ self._load_history(info_jid, info_acc)
+ yield True
keys.sort()
+ yield False
def _get_account_for_jid(self, jid):
'''Return the corresponding account of the jid.
@@ -202,10 +213,6 @@ class HistoryWindow:
return account
def on_history_window_destroy(self, widget):
- if self.mark_days_idle_call_id:
- # if user destroys the window, and we have a generator filling mark days
- # stop him!
- gobject.source_remove(self.mark_days_idle_call_id)
self.history_textview.del_handlers()
del gajim.interface.instances['logs']
@@ -265,11 +272,10 @@ class HistoryWindow:
# select logs for last date we have logs with contact
self.calendar.set_sensitive(True)
- self.calendar.emit('month-changed')
- lastlog = gajim.logger.get_last_date_that_has_logs(self.jid, self.account)
+ last_log = \
+ gajim.logger.get_last_date_that_has_logs(self.jid, self.account)
- tim = lastlog
- date = time.localtime(tim)
+ date = time.localtime(last_log)
y, m, d = date[0], date[1], date[2]
gtk_month = gtkgui_helpers.make_python_month_gtk_month(m)
@@ -305,31 +311,21 @@ class HistoryWindow:
month = gtkgui_helpers.make_gtk_month_python_month(month)
self._add_lines_for_date(year, month, day)
- def do_possible_mark_for_days_in_this_month(self, widget, year, month):
- '''this is a generator and does pseudo-threading via idle_add()
- so it runs progressively! yea :)
- asks for days in this month if they have logs it bolds them (marks them)'''
- weekday, days_in_this_month = calendar.monthrange(year, month)
- log_days = gajim.logger.get_days_with_logs(self.jid, year,
- month, days_in_this_month, self.account)
- for day in log_days:
- widget.mark_day(day)
- yield True
- yield False
-
def on_calendar_month_changed(self, widget):
+ '''asks for days in this month if they have logs it bolds them (marks
+ them)
+ '''
year, month, day = widget.get_date() # integers
# in gtk January is 1, in python January is 0,
# I want the second
# first day of month is 1 not 0
- if self.mark_days_idle_call_id:
- # if user changed month, and we have a generator filling mark days
- # stop him from marking dates for the previously selected month
- gobject.source_remove(self.mark_days_idle_call_id)
widget.clear_marks()
month = gtkgui_helpers.make_gtk_month_python_month(month)
- self.mark_days_idle_call_id = gobject.idle_add(
- self.do_possible_mark_for_days_in_this_month(widget, year, month).next)
+ weekday, days_in_this_month = calendar.monthrange(year, month)
+ log_days = gajim.logger.get_days_with_logs(self.jid, year,
+ month, days_in_this_month, self.account)
+ for day in log_days:
+ widget.mark_day(day)
def _get_string_show_from_constant_int(self, show):
if show == constants.SHOW_ONLINE:
@@ -480,7 +476,8 @@ class HistoryWindow:
message = row[4]
local_time = time.localtime(tim)
date = time.strftime('%x', local_time)
- # jid (to which log is assigned to), name, date, message, time (full unix time)
+ # jid (to which log is assigned to), name, date, message,
+ # time (full unix time)
model.append((jid, contact_name, date, message, tim))
def on_query_combobox_changed(self, widget):
@@ -573,9 +570,12 @@ class HistoryWindow:
def open_history(self, jid, account):
'''Load chat history of the specified jid'''
+ self.jid_entry.set_text(jid)
if account and account not in self.accounts_seen_online:
# Update dict to not only show bare jid
- self._fill_completion_dict
- self.jid_entry.set_text(jid)
- self._load_history(jid, account)
+ gobject.idle_add(self._fill_completion_dict().next)
+ else:
+ # Only in that case because it's called by self._fill_completion_dict()
+ # otherwise
+ self._load_history(jid, account)
self.results_window.set_property('visible', False)
diff --git a/src/message_control.py b/src/message_control.py
index ebff23368..d7092f76a 100644
--- a/src/message_control.py
+++ b/src/message_control.py
@@ -134,12 +134,17 @@ class MessageControl:
new_key = session.thread_id
if oldsession:
- self.parent_win.change_thread_key(
- self.contact.jid, self.account,
+ jid = self.contact.jid
+ if self.resource:
+ jid += '/' + self.resource
+
+ self.parent_win.change_thread_key(jid, self.account,
oldsession.thread_id, new_key)
if oldsession.enable_encryption:
self.print_esession_details()
+ else:
+ self.parent_win.move_from_sessionless(self)
def send_message(self, message, keyID = '', type = 'chat',
chatstate = None, msg_id = None, composing_xep = None, resource = None,
@@ -148,56 +153,18 @@ class MessageControl:
# Doesn't return None if error
jid = self.contact.jid
original_message = message
+ conn = gajim.connections[self.account]
if not self.session:
- sess = gajim.connections[self.account].make_new_session(jid)
- self.set_session(sess)
- self.parent_win.move_from_sessionless(self)
+ sess = conn.find_controlless_session(jid)
+
+ if not sess:
+ sess = conn.make_new_session(jid)
- xep_200 = bool(self.session) and self.session.enable_encryption
-
- if gajim.otr_module and not xep_200 and (jid not in gajim.otr_dont_append_tag):
- if type == 'chat' and isinstance(message, unicode):
- d = {'kwargs': {'keyID': keyID, 'type': type,
- 'chatstate': chatstate,
- 'msg_id': msg_id,
- 'composing_xep': composing_xep,
- 'resource': self.resource,
- 'user_nick': user_nick,
- 'session': self.session,
- 'original_message': original_message},
- 'account': self.account}
-
- new_msg = gajim.otr_module.otrl_message_sending(
- self.session.conn.otr_userstates,
- (gajim.otr_ui_ops, d),
- gajim.get_jid_from_account(
- self.account).encode(),
- gajim.OTR_PROTO,
- self.contact.get_full_jid().encode(),
- message.encode(), None,
- (gajim.otr_add_appdata, self.account))
-
- ctx = gajim.otr_module.otrl_context_find(
- self.session.conn.otr_userstates,
- self.contact.get_full_jid().encode(),
- gajim.get_jid_from_account(
- self.account).encode(),
- gajim.OTR_PROTO, 1,
- (gajim.otr_add_appdata,
- self.account))[0]
-
- # we send all because inject_message can filter
- # on HTML stuff then
- gajim.otr_module.otrl_message_fragment_and_send(
- (gajim.otr_ui_ops, d), ctx, new_msg,
- gajim.otr_module.OTRL_FRAGMENT_SEND_ALL)
- return
+ self.set_session(sess)
# Send and update history
- return gajim.connections[self.account].send_message(jid,
- message, keyID, type = type, chatstate = chatstate,
- msg_id = msg_id, composing_xep = composing_xep,
+ return conn.send_message(jid, message, keyID, type = type,
+ chatstate = chatstate, msg_id = msg_id, composing_xep = composing_xep,
resource = self.resource, user_nick = user_nick,
- session = self.session,
- original_message = original_message)
+ session = self.session, original_message = original_message)
diff --git a/src/message_window.py b/src/message_window.py
index 21c362af5..a457318d2 100644
--- a/src/message_window.py
+++ b/src/message_window.py
@@ -448,6 +448,7 @@ class MessageWindow(object):
def set_active_tab(self, ctrl):
ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page)
+ self.window.present()
def remove_tab(self, ctrl, method, reason = None, force = False):
'''reason is only for gc (offline status message)
@@ -692,10 +693,13 @@ class MessageWindow(object):
del self.sessionless_ctrls[acct][jid][idx]
+ if len(self.sessionless_ctrls[acct][jid]) == 0:
+ del self.sessionless_ctrls[acct][jid]
+
if not self._controls.has_key(acct):
self._controls[acct] = {}
- if not self.sessionless_ctrls[acct].has_key(jid):
+ if not self._controls[acct].has_key(jid):
self._controls[acct][jid] = {}
thread_id = ctrl.session.thread_id
diff --git a/src/osx/__init__.py b/src/osx/__init__.py
index ee21386cb..263633712 100644
--- a/src/osx/__init__.py
+++ b/src/osx/__init__.py
@@ -1,15 +1,12 @@
import sys, commands
from network_manager_listener import device_now_active, device_no_longer_active
-import nsapp
-
-if sys.platform != "darwin":
- raise ImportError("System platform is not OS/X")
+if sys.platform != 'darwin':
+ raise ImportError('System platform is not OS X')
net_device_active = True
-
###
### Utility functions
###
@@ -21,6 +18,7 @@ def checkPID(pid, procname):
return True
return False
+import nsapp
def init():
nsapp.init()
diff --git a/src/otr_windows.py b/src/otr_windows.py
deleted file mode 100644
index 2b3c5ab18..000000000
--- a/src/otr_windows.py
+++ /dev/null
@@ -1,357 +0,0 @@
-#!/usr/bin/env python
-## otr_windows.py
-##
-##
-## Copyright (C) 2008 Kjell Braden <fnord@pentabarf.de>
-##
-## This file is part of Gajim.
-##
-## Gajim is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 3 only.
-##
-## Gajim is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-##
-
-import gtkgui_helpers
-from common import gajim
-
-our_fp_text = _('Your fingerprint:\n' \
- '<span weight="bold" face="monospace">%s</span>')
-their_fp_text = _('Purported fingerprint for %s:\n' \
- '<span weight="bold" face="monospace">%s</span>')
-
-class ContactOtrSMPWindow:
- def gw(self, n):
- # shorthand for self.xml.get_widget(n)
- return self.xml.get_widget(n)
-
- def __init__(self, fjid, account):
- self.fjid = fjid
- self.account = account
-
- self.xml = gtkgui_helpers.get_glade('contact_otr_window.glade')
- self.window = self.xml.get_widget('otr_smp_window')
-
- # the contact may be unknown to gajim if ContactOtrSMPWindow
- # is created very early
- self.contact = gajim.contacts.get_contact_from_full_jid(
- account, fjid)
- if self.contact:
- self.window.set_title(_('OTR settings for %s') % \
- self.contact.get_full_jid())
-
- self.ctx = gajim.otr_module.otrl_context_find(
- gajim.connections[self.account].otr_userstates,
- self.fjid.encode(), gajim.get_jid_from_account(
- self.account).encode(), gajim.OTR_PROTO, 1,
- (gajim.otr_add_appdata, self.account))[0]
-
- self.gw('smp_cancel_button').connect('clicked',
- self._on_destroy)
- self.gw('smp_ok_button').connect('clicked', self._apply)
-
- def show(self, response):
- # re-initialize if contact was unknown when we
- # initially initialized
- if not self.contact:
- self.__init__(self.fjid, self.account)
- # the contact MUST be known when showing the dialog
- assert(self.contact)
-
- self.smp_running = False
- self.finished = False
-
- self.gw('smp_cancel_button').set_sensitive(True)
- self.gw('smp_ok_button').set_sensitive(True)
- self.gw('progressbar').set_fraction(0)
- self.gw('secret_entry').set_text('')
-
- self.response = response
- if response:
- self.gw('desc_label').set_markup(_('<b>%s is trying ' \
- 'to authenticate you using a secret only ' \
- 'known to him/her and you.</b> Please enter ' \
- 'your secret below.') % \
- self.contact.get_full_jid())
- else:
- self.gw('desc_label').set_markup(_('<b>You are ' \
- 'trying to authenticate %s using a secret' \
- 'only known to him/her and yourself.</b>' \
- 'Please enter your secret below.') % \
- self.contact.get_full_jid())
-
- self.window.show_all()
-
- def _abort(self, text=None):
- self.smp_running = False
- gajim.otr_module.otrl_message_abort_smp(
- gajim.connections[self.account].otr_userstates,
- (gajim.otr_ui_ops, {'account': self.account}), self.ctx)
- if text:
- gajim.otr_ui_ops.gajim_log(text, self.account,
- self.contact.get_full_jid())
-
- def _finish(self, text):
- self.smp_running = False
- self.finished = True
- self.gw('smp_cancel_button').set_sensitive(False)
- self.gw('smp_ok_button').set_sensitive(True)
- self.gw('progressbar').set_fraction(1)
- gajim.otr_ui_ops.gajim_log(text, self.account,
- self.contact.get_full_jid())
- self.gw('desc_label').set_markup(text)
- for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(
- self.contact.jid, self.account):
- ctrl.update_otr(True)
- gajim.otr_ui_ops.write_fingerprints({'account': self.account})
-
- def handle_tlv(self, tlvs):
- if not self.contact:
- self.__init__(self.fjid, self.account)
-
- if tlvs:
- nextTLV = self.ctx.smstate.nextExpected;
- tlv = gajim.otr_module.otrl_tlv_find(tlvs,
- gajim.otr_module.OTRL_TLV_SMP1)
- if tlv:
- if nextTLV != \
- gajim.otr_module.OTRL_SMP_EXPECT1:
- self._abort()
- else:
- self.show(True)
- self.gw('progressbar'). \
- set_fraction(0.3)
- tlv = gajim.otr_module.otrl_tlv_find(tlvs,
- gajim.otr_module.OTRL_TLV_SMP2)
- if tlv:
- if nextTLV != gajim.otr_module.OTRL_SMP_EXPECT2:
- self._abort()
- else:
- self.ctx.smstate.nextExpected = \
- gajim.otr_module. \
- OTRL_SMP_EXPECT4
- self.gw('progressbar').set_fraction(0.6)
- tlv = gajim.otr_module.otrl_tlv_find(tlvs,
- gajim.otr_module.OTRL_TLV_SMP3)
- if tlv:
- if nextTLV != gajim.otr_module.OTRL_SMP_EXPECT3:
- self._abort()
- else:
- self.ctx.smstate.nextExpected = \
- gajim.otr_module. \
- OTRL_SMP_EXPECT1;
- if self.ctx.active_fingerprint.trust:
- self._finish(_('SMP ' \
- 'verifying succeeded'))
- else:
- self._finish(_('SMP ' \
- 'verifying failed'))
- tlv = gajim.otr_module.otrl_tlv_find(tlvs,
- gajim.otr_module.OTRL_TLV_SMP4)
- if tlv:
- if nextTLV != gajim.otr_module.OTRL_SMP_EXPECT4:
- self._abort()
- else:
- self.ctx.smstate.nextExpected = \
- gajim.otr_module. \
- OTRL_SMP_EXPECT1;
- if self.ctx.active_fingerprint.trust:
- self._finish(_('SMP ' \
- 'verifying succeeded'))
- else:
- self._finish(_('SMP ' \
- 'verifying failed'))
- tlv = gajim.otr_module.otrl_tlv_find(tlvs,
- gajim.otr_module.OTRL_TLV_SMP_ABORT)
- if tlv:
- self._finish(_('SMP verifying aborted'))
-
- def _on_destroy(self, widget):
- if self.smp_running:
- self._abort(_('user aborted SMP authentication'))
- self.window.hide_all()
-
- def _apply(self, widget):
- if self.finished:
- self.window.hide_all()
- return
- secret = self.gw('secret_entry').get_text()
- if self.response:
- gajim.otr_module.otrl_message_respond_smp(
- gajim.connections[self.account].otr_userstates,
- (gajim.otr_ui_ops, {'account': self.account}),
- self.ctx, secret)
- else:
- gajim.otr_module.otrl_message_initiate_smp(
- gajim.connections[self.account].otr_userstates,
- (gajim.otr_ui_ops, {'account': self.account}),
- self.ctx, secret)
- self.gw('progressbar').set_fraction(0.3)
- self.smp_running = True
- widget.set_sensitive(False)
-
-class ContactOtrWindow:
- def gw(self, n):
- # shorthand for self.xml.get_widget(n)
- return self.xml.get_widget(n)
-
- def __init__(self, contact, account, ctrl=None):
- self.contact = contact
- self.account = account
- self.ctrl = ctrl
-
- self.ctx = gajim.otr_module.otrl_context_find(
- gajim.connections[self.account].otr_userstates,
- self.contact.get_full_jid().encode(),
- gajim.get_jid_from_account(self.account).encode(),
- gajim.OTR_PROTO, 1, (gajim.otr_add_appdata,
- self.account))[0]
-
- self.xml = gtkgui_helpers.get_glade('contact_otr_window.glade')
- self.window = self.xml.get_widget('otr_settings_window')
-
- self.gw('settings_cancel_button').connect('clicked',
- self._on_destroy)
- self.gw('settings_ok_button').connect('clicked', self._apply)
- self.gw('otr_default_checkbutton').connect('toggled',
- self._otr_default_checkbutton_toggled)
-
- self.window.set_title(_('OTR settings for %s') % \
- self.contact.get_full_jid())
-
- # always set the label containing our fingerprint
- self.gw('our_fp_label').set_markup(our_fp_text % \
- gajim.otr_module.otrl_privkey_fingerprint(
- gajim.connections[self.account].otr_userstates,
- gajim.get_jid_from_account(self.account).encode(),
- gajim.OTR_PROTO))
-
- if self.ctx.msgstate != \
- gajim.otr_module.OTRL_MSGSTATE_ENCRYPTED:
- # make the fingerprint widgets insensitive
- # when not encrypted
- for widget in self.gw('otr_fp_vbox').get_children():
- widget.set_sensitive(False)
- # show that the fingerprint is unknown
- self.gw('their_fp_label').set_markup(
- their_fp_text % (self.contact.get_full_jid(),
- _('unknown')))
- self.gw('verified_combobox').set_active(-1)
- else:
- # make the fingerprint widgets sensitive when encrypted
- for widget in self.gw('otr_fp_vbox').get_children():
- widget.set_sensitive(True)
- # show their fingerprint
- self.gw('their_fp_label').set_markup(
- their_fp_text%(self.contact.get_full_jid(),
- gajim.otr_module.otrl_privkey_hash_to_human(
- self.ctx.active_fingerprint.fingerprint)))
- # set the trust combobox
- if self.ctx.active_fingerprint.trust:
- self.gw('verified_combobox').set_active(1)
- else:
- self.gw('verified_combobox').set_active(0)
-
- otr_flags = gajim.config.get_per('contacts', self.contact.jid,
- 'otr_flags')
-
- if otr_flags >= 0:
- self.gw('otr_default_checkbutton').set_active(0)
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(True)
- else:
- # per-user settings not available,
- # using default settings
- otr_flags = gajim.config.get_per('accounts',
- self.account, 'otr_flags')
- self.gw('otr_default_checkbutton').set_active(1)
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(False)
-
- self.gw('otr_policy_allow_v1_checkbutton').set_active(
- otr_flags & gajim.otr_module.OTRL_POLICY_ALLOW_V1)
- self.gw('otr_policy_allow_v2_checkbutton').set_active(
- otr_flags & gajim.otr_module.OTRL_POLICY_ALLOW_V2)
- self.gw('otr_policy_require_checkbutton').set_active(
- otr_flags & gajim.otr_module.OTRL_POLICY_REQUIRE_ENCRYPTION)
- self.gw('otr_policy_send_tag_checkbutton').set_active(
- otr_flags & \
- gajim.otr_module.OTRL_POLICY_SEND_WHITESPACE_TAG)
- self.gw('otr_policy_start_on_tag_checkbutton').set_active(
- otr_flags & \
- gajim.otr_module.OTRL_POLICY_WHITESPACE_START_AKE)
- self.gw('otr_policy_start_on_error_checkbutton').set_active(
- otr_flags & \
- gajim.otr_module.OTRL_POLICY_ERROR_START_AKE)
-
- self.window.show_all()
-
- def _on_destroy(self, widget):
- self.window.destroy()
-
- def _apply(self, widget):
- # -1 when nothing is selected
- # (ie. the connection is not encrypted)
- trust_state = self.gw('verified_combobox').get_active()
- if trust_state == 1 and not self.ctx.active_fingerprint.trust:
- gajim.otr_module.otrl_context_set_trust(
- self.ctx.active_fingerprint, 'verified')
- gajim.otr_ui_ops.write_fingerprints(
- {'account': self.account})
- elif trust_state == 0:
- gajim.otr_module.otrl_context_set_trust(
- self.ctx.active_fingerprint, '')
- gajim.otr_ui_ops.write_fingerprints(
- {'account': self.account})
-
- if not self.ctrl:
- self.ctrl = gajim.interface.msg_win_mgr.get_control(
- self.contact.jid, self.account)
- if self.ctrl:
- self.ctrl.update_otr(True)
-
- if self.gw('otr_default_checkbutton').get_active():
- # default is enabled, so remove any user-specific
- # settings if available
- gajim.config.set_per('contacts',
- self.contact.jid, 'otr_flags', -1)
- else:
- # build the flags using the checkboxes
- flags = 0
- flags += self.gw('otr_policy_allow_v1_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_ALLOW_V1
- flags += self.gw('otr_policy_allow_v2_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_ALLOW_V2
- flags += self.gw('otr_policy_require_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_REQUIRE_ENCRYPTION
- flags += self.gw('otr_policy_send_tag_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_SEND_WHITESPACE_TAG
- flags += self.gw(
- 'otr_policy_start_on_tag_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_WHITESPACE_START_AKE
- flags += self.gw(
- 'otr_policy_start_on_error_checkbutton'). \
- get_active() and gajim.otr_module. \
- OTRL_POLICY_ERROR_START_AKE
-
- gajim.config.add_per('contacts', self.contact.jid)
- gajim.config.set_per('contacts', self.contact.jid,
- 'otr_flags', flags)
-
- self._on_destroy(widget)
-
- def _otr_default_checkbutton_toggled(self, widget):
- for w in self.gw('otr_settings_vbox').get_children():
- w.set_sensitive(not widget.get_active())
diff --git a/src/roster_window.py b/src/roster_window.py
index bee28bd63..935067cbb 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -61,9 +61,11 @@ if dbus_support.supported:
import dbus
from lastfm_track_listener import LastFMTrackListener
-if sys.platform == 'darwin':
+try:
from osx import syncmenu
-
+except ImportError:
+ pass
+
#(icon, name, type, jid, account, editable, second pixbuf)
(
C_IMG, # image to show state (online, new message etc)
@@ -84,7 +86,7 @@ class RosterWindow:
Keyword arguments:
name -- the account name
model -- the data model (default TreeFilterModel)
-
+
'''
if not model:
model = self.modelfilter
@@ -109,7 +111,7 @@ class RosterWindow:
account -- the account name
account_iter -- the iter of the account the model (default None)
model -- the data model (default TreeFilterModel)
-
+
'''
if not model:
model = self.modelfilter
@@ -123,7 +125,7 @@ class RosterWindow:
break
group_iter = model.iter_next(group_iter)
return group_iter
-
+
def _get_self_contact_iter(self, jid, account, model = None):
''' Return the gtk.TreeIter of SelfContact or None if not found.
@@ -132,14 +134,14 @@ class RosterWindow:
jid -- the jid of SelfContact
account -- the account of SelfContact
model -- the data model (default TreeFilterModel)
-
+
'''
-
+
if not model:
model = self.modelfilter
iterAcct = self._get_account_iter(account, model)
iterC = model.iter_children(iterAcct)
-
+
# There might be several SelfContacts in merged account view
while iterC:
if model[iterC][C_TYPE] != 'self_contact':
@@ -159,7 +161,7 @@ class RosterWindow:
account -- the account
contact -- the contact (default None)
model -- the data model (default TreeFilterModel)
-
+
'''
if not model:
model = self.modelfilter
@@ -179,12 +181,9 @@ class RosterWindow:
# We don't know this contact
return
- groups = contact.groups
- if not groups:
- groups = [_('General')]
acct = self._get_account_iter(account, model)
found = [] # the contact iters. One per group
- for group in groups:
+ for group in contact.groups:
group_iter = self._get_group_iter(group, account, acct, model)
contact_iter = model.iter_children(group_iter)
@@ -199,7 +198,7 @@ class RosterWindow:
elif model.iter_has_child(contact_iter):
# it's a big brother and has children
contact_iter = model.iter_children(contact_iter)
- else:
+ else:
# try to find next contact:
# other contact in this group or brother contact
next_contact_iter = model.iter_next(contact_iter)
@@ -214,14 +213,14 @@ class RosterWindow:
# we tested all contacts in this group
contact_iter = None
return found
-
-
+
+
def _iter_is_separator(self, model, titer):
''' Return True if the given iter is a separator.
-
+
Keyword arguments:
model -- the data model
- iter -- the gtk.TreeIter to test
+ iter -- the gtk.TreeIter to test
'''
if model[titer][0] == 'SEPARATOR':
return True
@@ -230,7 +229,7 @@ class RosterWindow:
def _iter_contact_rows(self, model = None):
'''Iterate over all contact rows in given model.
-
+
Keyword argument
model -- the data model (default TreeFilterModel)
'''
@@ -246,35 +245,36 @@ class RosterWindow:
contact_iter = model.iter_next(contact_iter)
group_iter = model.iter_next(group_iter)
account_iter = model.iter_next(account_iter)
-
-
-#############################################################################
+
+
+#############################################################################
### Methods for adding and removing roster window items
#############################################################################
-
+
def add_account(self, account):
'''Add account to roster and draw it. Do nothing if it is already in.'''
if self._get_account_iter(account):
- # Will happen on reconnect or for merged accounts
+ # Will happen on reconnect or for merged accounts
return
-
+
if self.regroup:
# Merged accounts view
show = helpers.get_global_show()
- self.model.append(None, [gajim.interface.jabber_state_images['16'][show],
- _('Merged accounts'), 'account', '', 'all', None, None])
+ self.model.append(None, [gajim.interface.jabber_state_images['16'][
+ show], _('Merged accounts'), 'account', '', 'all', None, None])
else:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
our_jid = gajim.get_jid_from_account(account)
tls_pixbuf = None
if gajim.account_is_securely_connected(account):
- tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
- gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
+ # the only way to create a pixbuf from stock
+ tls_pixbuf = self.window.render_icon(
+ gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
- self.model.append(None, [gajim.interface.jabber_state_images['16'][show],
- gobject.markup_escape_text(account),
- 'account', our_jid, account, None, tls_pixbuf])
+ self.model.append(None, [gajim.interface.jabber_state_images['16'][
+ show], gobject.markup_escape_text(account), 'account', our_jid,
+ account, None, tls_pixbuf])
self.draw_account(account)
@@ -286,7 +286,7 @@ class RosterWindow:
c1 = time.clock()
self.starting = True
jids = gajim.contacts.get_jid_list(account)
-
+
self.tree.freeze_child_notify()
c5 = time.clock()
for jid in jids:
@@ -306,26 +306,26 @@ class RosterWindow:
self.draw_account(account)
self.starting = False
c10 = time.clock()
-
+
if jids:
c4 = time.clock()
-
+
print ""
print "--- Add account contacts of %s ---------" % account
- print "Total Time", c4-c1
- print "Add contact without draw", c6-c5
+ print "Total Time", c4-c1
+ print "Add contact without draw", c6-c5
print "Draw groups and account", c10-c9
print "--- contacts added -----------------------------"
print ""
-
+
def _add_entity(self, contact, account, groups = None,
big_brother_contact = None, big_brother_account = None):
'''Add the given contact to roster data model.
-
+
Contact is added regardless if he is already in roster or not.
Return list of newly added iters.
-
+
Keyword arguments:
contact -- the contact to add
account -- the contacts account
@@ -340,9 +340,9 @@ class RosterWindow:
parent_iters = self._get_contact_iter(big_brother_contact.jid,
big_brother_account, big_brother_contact, self.model)
assert len(parent_iters) > 0,\
- "Big brother is not yet in roster!"
+ 'Big brother is not yet in roster!'
- # Do not confuse get_contact_iter
+ # Do not confuse get_contact_iter
# Sync groups of family members
contact.groups = big_brother_contact.groups[:]
@@ -353,13 +353,9 @@ class RosterWindow:
else:
# We are a normal contact. Add us to our groups.
if not groups:
- if contact.is_observer():
- contact.groups = [_('Observers')]
groups = contact.groups
- if not groups:
- groups = [_('General')]
for group in groups:
- child_iterG = self._get_group_iter(group, account, model = self.model)
+ child_iterG = self._get_group_iter(group, account, model=self.model)
if not child_iterG:
# Group is not yet in roster, add it!
child_iterA = self._get_account_iter(account, self.model)
@@ -375,35 +371,30 @@ class RosterWindow:
typestr = 'groupchat'
else:
typestr = 'contact'
-
+
# we add some values here. see draw_contact for more
i_ = self.model.append(child_iterG, (None, contact.get_shown_name(),
typestr, contact.jid, account, None, None))
added_iters.append(i_)
# Restore the group expand state
- # FIXME path may be invalid at this point!
- path = self.model.get_path(child_iterG)
if account + group in self.collapsed_rows:
is_expanded = False
- self.tree.collapse_row(path)
else:
is_expanded = True
- self.tree.expand_row(path, False)
if group not in gajim.groups[account]:
gajim.groups[account][group] = {'expand': is_expanded}
-
- assert len(added_iters), "%s has not been added to roster!" % contact.jid
- return added_iters
-
-
+
+ assert len(added_iters), '%s has not been added to roster!' % contact.jid
+ return added_iters
+
def _remove_entity(self, contact, account, groups = None):
'''Remove the given contact from roster data model.
-
+
Empty groups after contact removal are removed too.
Return False if contact still has children and deletion was not performed.
Return True on success.
-
+
Keyword arguments:
contact -- the contact to add
account -- the contacts account
@@ -411,7 +402,7 @@ class RosterWindow:
'''
iters = self._get_contact_iter(contact.jid, account, contact, self.model)
- assert iters, "%s shall be removed but is not in roster" % contact.jid
+ assert iters, '%s shall be removed but is not in roster' % contact.jid
parent_iter = self.model.iter_parent(iters[0])
parent_type = self.model[parent_iter][C_TYPE]
@@ -419,9 +410,10 @@ class RosterWindow:
if groups:
# Only remove from specified groups
all_iters = iters[:]
- group_iters = [self._get_group_iter(group, account) for group in groups]
- iters = [titer for titer in all_iters
- if self.model.iter_parent(titer) in group_iters]
+ group_iters = [self._get_group_iter(group, account) for group in \
+ groups]
+ iters = [titer for titer in all_iters
+ if self.model.iter_parent(titer) in group_iters]
iter_children = self.model.iter_children(iters[0])
@@ -432,24 +424,23 @@ class RosterWindow:
# Remove us and empty groups from the model
for i in iters:
parent_i = self.model.iter_parent(i)
- self.model.remove(i)
-
if parent_type == 'group' and \
- self.model.iter_n_children(parent_i) == 0:
+ self.model.iter_n_children(parent_i) == 1:
group = self.model[parent_i][C_JID].decode('utf-8')
if gajim.groups[account].has_key(group):
del gajim.groups[account][group]
self.model.remove(parent_i)
+ else:
+ self.model.remove(i)
return True
-
def _add_metacontact_family(self, family, account):
'''Add the give Metacontact family to roster data model.
-
- Add Big Brother to his groups and all others under him.
+
+ Add Big Brother to his groups and all others under him.
Return list of all added (contact, account) tuples with Big Brother
as first element.
-
+
Keyword arguments:
family -- the family, see Contacts.get_metacontacts_family()
'''
@@ -459,10 +450,11 @@ class RosterWindow:
nearby_family = family
else:
# we want one nearby_family per account
- nearby_family = [data for data in family
+ nearby_family = [data for data in family
if account == data['account']]
- big_brother_data = gajim.contacts.get_metacontacts_big_brother(nearby_family)
+ big_brother_data = gajim.contacts.get_metacontacts_big_brother(
+ nearby_family)
big_brother_jid = big_brother_data['jid']
big_brother_account = big_brother_data['account']
big_brother_contact = gajim.contacts.get_first_contact_from_jid(
@@ -470,9 +462,10 @@ class RosterWindow:
assert len(self._get_contact_iter(big_brother_jid, big_brother_account,
big_brother_contact, self.model)) == 0,\
- "Big brother %s already in roster \n Family: %s" % (big_brother_jid, family)
+ 'Big brother %s already in roster \n Family: %s' % (big_brother_jid,
+ family)
self._add_entity(big_brother_contact, big_brother_account)
-
+
brothers = []
# Filter family members
for data in nearby_family:
@@ -486,20 +479,19 @@ class RosterWindow:
if not _contact:
# Corresponding account is not connected
continue
-
+
assert len(self._get_contact_iter(_jid, _account, _contact, self.model)
) == 0, "%s already in roster. \n Family: " % (_jid, nearby_family)
- self._add_entity(_contact, _account, big_brother_contact = big_brother_contact,
- big_brother_account = big_brother_account)
+ self._add_entity(_contact, _account, big_brother_contact = \
+ big_brother_contact, big_brother_account=big_brother_account)
brothers.append((_contact, _account))
brothers.insert(0, (big_brother_contact, big_brother_account))
return brothers
-
def _remove_metacontact_family(self, family, account):
'''Remove the given Metacontact family from roster data model.
-
+
See Contacts.get_metacontacts_family() and RosterWindow._remove_entity()
'''
if self.regroup:
@@ -507,30 +499,30 @@ class RosterWindow:
nearby_family = family
else:
# remove nearby_family per account
- nearby_family = [data for data in family
+ nearby_family = [data for data in family
if account == data['account']]
- # Family might has changed (actual big brother not on top).
+ # Family might has changed (actual big brother not on top).
# Remove childs first then big brother
family_in_roster = False
for data in nearby_family:
_account = data['account']
_jid = data['jid']
_contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
-
+
iters = self._get_contact_iter(_jid, _account, _contact, self.model)
if not iters or not _contact:
# Family might not be up to date.
# Only try to remove what is actually in the roster
continue
- assert iters, "%s shall be removed but is not in roster \
- \n Family: %s" % (_jid, family)
+ assert iters, '%s shall be removed but is not in roster \
+ \n Family: %s' % (_jid, family)
family_in_roster = True
parent_iter = self.model.iter_parent(iters[0])
parent_type = self.model[parent_iter][C_TYPE]
-
+
if parent_type != 'contact':
# The contact on top
old_big_account = _account
@@ -539,68 +531,64 @@ class RosterWindow:
continue
ok = self._remove_entity(_contact, _account)
- assert ok, "%s was not removed" % _jid
- assert len(self._get_contact_iter(_jid, _account, _contact, self.model)) == 0,\
- "%s is removed but still in roster" % _jid
-
+ assert ok, '%s was not removed' % _jid
+ assert len(self._get_contact_iter(_jid, _account, _contact,
+ self.model)) == 0, '%s is removed but still in roster' % _jid
+
if not family_in_roster:
return False
- iters = self._get_contact_iter(old_big_jid, old_big_account, old_big_contact,
- self.model)
- assert len(iters) > 0, "Old Big Brother %s is not in roster anymore" % old_big_jid
+ iters = self._get_contact_iter(old_big_jid, old_big_account,
+ old_big_contact, self.model)
+ assert len(iters) > 0, 'Old Big Brother %s is not in roster anymore' % \
+ old_big_jid
assert not self.model.iter_children(iters[0]),\
- "Old Big Brother %s still has children" % old_big_jid
-
- # This one is strange but necessary:
- # Refilter filtered model to not crash hard. It thinks it still has children.
- self.refilter_shown_roster_items()
-
+ 'Old Big Brother %s still has children' % old_big_jid
+
ok = self._remove_entity(old_big_contact, old_big_account)
assert ok, "Old Big Brother %s not removed" % old_big_jid
- assert len(self._get_contact_iter(old_big_jid, old_big_account, old_big_contact,
- self.model)) == 0,\
- "Old Big Brother %s is removed but still in roster" % old_big_jid
-
+ assert len(self._get_contact_iter(old_big_jid, old_big_account,
+ old_big_contact, self.model)) == 0,\
+ 'Old Big Brother %s is removed but still in roster' % old_big_jid
+
return True
-
-
+
def _add_self_contact(self, account):
'''Add account's SelfContact to roster and draw it and the account.
-
+
Return the SelfContact contact instance
'''
jid = gajim.get_jid_from_account(account)
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
-
- assert len(self._get_contact_iter(jid, account, contact, self.model)) == 0,\
- "Self contact %s already in roster" % jid
+
+ assert len(self._get_contact_iter(jid, account, contact, self.model)) == \
+ 0, 'Self contact %s already in roster' % jid
child_iterA = self._get_account_iter(account, self.model)
self.model.append(child_iterA, (None, gajim.nicks[account],
'self_contact', jid, account, None, None))
-
+
self.draw_contact(jid, account)
self.draw_avatar(jid, account)
self.draw_account(account)
return contact
-
-
+
+
def add_contact(self, jid, account):
'''Add contact to roster and draw him.
-
+
Add contact to all its group and redraw the groups, the contact and the
account. If it's a Metacontact, add and draw the whole family.
- Do nothing if the contact is already in roster.
-
- Return the added contact instance. If it is a Metacontact return
+ Do nothing if the contact is already in roster.
+
+ Return the added contact instance. If it is a Metacontact return
Big Brother.
-
+
Keyword arguments:
jid -- the contact's jid or SelfJid to add SelfContact
account -- the corresponding account.
-
+
'''
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if len(self._get_contact_iter(jid, account, contact, self.model)):
@@ -611,14 +599,14 @@ class RosterWindow:
if contact.resource != gajim.connections[account].server_resource:
return self._add_self_contact(account)
return
-
+
is_observer = contact.is_observer()
if is_observer:
# if he has a tag, remove it
tag = gajim.contacts.get_metacontacts_tag(account, jid)
if tag:
gajim.contacts.remove_metacontact(account, jid)
-
+
# Add contact to roster
family = gajim.contacts.get_metacontacts_family(account, jid)
contacts = []
@@ -635,16 +623,11 @@ class RosterWindow:
self._add_entity(contact, account)
# Draw the contact and its groups contact
- if is_observer:
- contact.groups = [_('Observers')]
- groups = contact.groups
- if not groups:
- groups = [_('General')]
if not self.starting:
for c, acc in contacts:
self.draw_contact(c.jid, acc)
self.draw_avatar(c.jid, acc)
- for group in groups:
+ for group in contact.groups:
self.draw_group(group, account)
self.draw_account(account)
@@ -653,18 +636,19 @@ class RosterWindow:
def remove_contact(self, jid, account, force = False, backend = False):
'''Remove contact from roster.
-
- Remove contact from all its group. Remove empty groups or redraw otherwise.
+
+ Remove contact from all its group. Remove empty groups or redraw
+ otherwise.
Draw the account.
If it's a Metacontact, remove the whole family.
- Do nothing if the contact is not in roster.
-
+ Do nothing if the contact is not in roster.
+
Keyword arguments:
jid -- the contact's jid or SelfJid to remove SelfContact
account -- the corresponding account.
force -- remove contact even it has pending evens (Default False)
backend -- also remove contact instance (Default False)
-
+
'''
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
@@ -685,22 +669,17 @@ class RosterWindow:
if family:
# We have a family. So we are a metacontact.
self._remove_metacontact_family(family, account)
- else:
+ else:
self._remove_entity(contact, account)
# Draw all groups of the contact
- groups = contact.groups
- if contact.is_observer():
- contact.groups = [_('Observers')]
- if not groups:
- groups = [_('General')]
- if backend:
+ if backend:
# Remove contact before redrawing, otherwise the old
# numbers will still be show
gajim.contacts.remove_jid(account, jid)
- for group in groups:
+ for group in contact.groups:
self.draw_group(group, account)
self.draw_account(account)
@@ -719,31 +698,30 @@ class RosterWindow:
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
else:
- contact.show = 'online'
- self.draw_contact(jid, account)
+ contact.show = 'online'
+ self.draw_contact(jid, account)
return contact
-
+
def remove_groupchat(self, jid, account):
'''Remove groupchat from roster and redraw account and group.'''
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
self.remove_contact(jid, account, force = True, backend = True)
return True
-
+
# TODO: This function is yet unused! Port to new API
def add_transport(self, jid, account):
'''Add transport to roster and draw it.
Return the added contact instance.'''
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- if contact is None:
+ if contact is None:
contact = gajim.contacts.create_contact(jid = jid, name = jid,
- groups = [_('Transports')], show = 'offline',
+ groups = [_('Transports')], show = 'offline',
status = 'offline', sub = 'from')
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
return contact
-
def remove_transport(self, jid, account):
'''Remove transport from roster and redraw account and group.'''
@@ -751,7 +729,7 @@ class RosterWindow:
self.remove_contact(jid, account, force = True, backend = True)
return True
- #FIXME:
+ #FIXME:
# We need to define a generic way to keep contacts in roster
# as long as they have pending events or as we
# still chat with them
@@ -764,7 +742,8 @@ class RosterWindow:
# Close chat window
msg_win = gajim.interface.msg_win_mgr.get_window(contact.jid,
account)
- for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid, account):
+ for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(
+ contact.jid, account):
msg_win.remove_tab(ctrl, msg_win.CLOSE_CLOSE_BUTTON)
else:
need_readd = True
@@ -778,19 +757,19 @@ class RosterWindow:
def add_contact_to_groups(self, jid, account, groups):
- '''Add contact to given groups and redraw them.
-
+ '''Add contact to given groups and redraw them.
+
Contact on server is updated too. When the contact has a family,
the action will be performed for all members.
-
+
Keyword Arguments:
jid -- the jid
account -- the corresponding account
groups -- list of Groups to add the contact to.
-
+
'''
self.remove_contact(jid, account, force = True)
-
+
for contact in gajim.contacts.get_contacts(account, jid):
for group in groups:
if group not in contact.groups:
@@ -806,16 +785,16 @@ class RosterWindow:
# self._adjust_group_expand_collapse_state(group, account)
def remove_contact_from_groups(self, jid, account, groups):
- '''Remove contact from given groups and redraw them.
-
+ '''Remove contact from given groups and redraw them.
+
Contact on server is updated too. When the contact has a family,
the action will be performed for all members.
-
+
Keyword Arguments:
jid -- the jid
account -- the corresponding account
groups -- list of Groups to remove the contact from
-
+
'''
self.remove_contact(jid, account, force = True)
@@ -826,13 +805,13 @@ class RosterWindow:
contact.groups.remove(group)
gajim.connections[account].update_contact(jid, contact.name,
contact.groups)
-
+
self.add_contact(jid, account)
for group in groups:
self.draw_group(group, account)
-
- # FIXME: maybe move to gajim.py
+
+ # FIXME: maybe move to gajim.py
def remove_newly_added(self, jid, account):
if jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(jid)
@@ -848,7 +827,7 @@ class RosterWindow:
if jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(jid)
self.draw_contact(jid, account)
-
+
#FIXME: integrate into add_contact()
def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''):
keyID = ''
@@ -862,11 +841,11 @@ class RosterWindow:
gajim.contacts.add_contact(account, contact)
self.add_contact(contact.jid, account)
return contact
-
-
-################################################################################
+
+
+################################################################################
### Methods for adding and removing roster window items
-################################################################################
+################################################################################
def draw_account(self, account):
child_iter = self._get_account_iter(account, self.model)
@@ -876,7 +855,7 @@ class RosterWindow:
num_of_accounts = gajim.get_number_of_connected_accounts()
num_of_secured = gajim.get_number_of_securely_connected_accounts()
-
+
if gajim.account_is_securely_connected(account) and not self.regroup or \
self.regroup and num_of_secured and num_of_secured == num_of_accounts:
tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
@@ -906,7 +885,7 @@ class RosterWindow:
self.model[child_iter][C_NAME] = account_name
return False
-
+
def draw_group(self, group, account):
child_iter = self._get_group_iter(group, account, model = self.model)
if not child_iter:
@@ -924,10 +903,10 @@ class RosterWindow:
nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
accounts = accounts, groups = [group])
text += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
-
+
self.model[child_iter][C_NAME] = gobject.markup_escape_text(text)
return False
-
+
def draw_parent_contact(self, jid, account):
child_iters = self._get_contact_iter(jid, account, self.model)
if not child_iters:
@@ -940,7 +919,7 @@ class RosterWindow:
parent_account = self.model[parent_iter][C_ACCOUNT].decode('utf-8')
self.draw_contact(parent_jid, parent_account)
return False
-
+
def draw_contact(self, jid, account, selected = False, focus = False):
'''draw the correct state image, name BUT not avatar'''
# focus is about if the roster window has toplevel-focus or not
@@ -950,16 +929,10 @@ class RosterWindow:
child_iters = self._get_contact_iter(jid, account, contact, self.model)
if not child_iters:
- return False
+ return False
name = gobject.markup_escape_text(contact.get_shown_name())
- groups = contact.groups
- if contact.is_observer():
- groups = [_('Observers')]
- elif not groups:
- groups = [_('General')]
-
# gets number of unread gc marked messages
if jid in gajim.interface.minimized_controls[account]:
nb_unread = len(gajim.events.get_events(account, jid,
@@ -971,13 +944,13 @@ class RosterWindow:
name = '%s *' % name
elif nb_unread > 1:
name = '%s [%s]' % (name, str(nb_unread))
-
+
# Strike name if blocked
strike = False
if jid in gajim.connections[account].blocked_contacts:
strike = True
else:
- for group in groups:
+ for group in contact.groups:
if group in gajim.connections[account].blocked_groups:
strike = True
break
@@ -1025,7 +998,7 @@ class RosterWindow:
name += \
'\n<span size="small" style="italic" foreground="%s">%s</span>' \
% (colorstring, gobject.markup_escape_text(status))
-
+
# Check if our metacontacts family has changed
brothers = []
family = gajim.contacts.get_metacontacts_family(account, jid)
@@ -1036,10 +1009,11 @@ class RosterWindow:
nearby_family = family
else:
# we want one nearby_family per account
- nearby_family = [data for data in family
+ nearby_family = [data for data in family
if account == data['account']]
- big_brother_data = gajim.contacts.get_metacontacts_big_brother(nearby_family)
+ big_brother_data = gajim.contacts.get_metacontacts_big_brother(
+ nearby_family)
big_brother_jid = big_brother_data['jid']
big_brother_account = big_brother_data['account']
@@ -1127,11 +1101,13 @@ class RosterWindow:
self.draw_contact(c.jid, acc)
self.draw_avatar(c.jid, acc)
- for group in groups:
+ for group in contact.groups:
# We need to make sure that _visible_func is called for
# our groups otherwise we might not be shown
iterG = self._get_group_iter(group, account, model = self.model)
- self.model[iterG][C_JID] = self.model[iterG][C_JID]
+ if iterG:
+ # it's not self contact
+ self.model[iterG][C_JID] = self.model[iterG][C_JID]
return False
@@ -1158,7 +1134,6 @@ class RosterWindow:
gajim.gc_connected[account][room_jid]:
win = gajim.interface.msg_win_mgr.get_window(room_jid, account)
ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
- win.window.present()
win.set_active_tab(ctrl)
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
return
@@ -1187,7 +1162,6 @@ class RosterWindow:
gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account)
gc_control = gc_win.get_gc_control(room_jid, account)
gc_win.set_active_tab(gc_control)
- gc_win.window.present()
gajim.connections[account].join_gc(nick, room_jid, password)
if password:
gajim.gc_passwords[room_jid] = password
@@ -1202,23 +1176,17 @@ class RosterWindow:
'''
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- groups = contact.groups
- if contact.is_observer():
- contact.groups = [_('Observers')]
- if not groups:
- groups = [_('General')]
-
self.draw_contact(jid, account)
self.draw_account(account)
- for group in groups:
+ for group in contact.groups:
self.draw_group(group, account)
# FIXME: Is this needed, Jim?
#self._adjust_group_expand_collapse_state(group, account)
def _idle_draw_jids_of_account(self, jids, account):
'''Draw given contacts and their avatars in a lazy fashion.
-
+
Keyword arguments:
jids -- a list of jids to draw
account -- the corresponding account
@@ -1232,23 +1200,32 @@ class RosterWindow:
print "Draw contact and avatar", time.clock() - t
print "-------------------------------"
yield False
-
+
t = time.clock()
task = _draw_all_contacts(jids, account, t)
gobject.idle_add(task.next)
-
- def draw_roster(self):
- '''clear and draw roster'''
- # clear the model, only if it is not empty
- if self.model:
- self.model.clear()
+
+ def setup_and_draw_roster(self):
+ '''create new empty model and draw roster'''
+ #(icon, name, type, jid, account, editable, avatar_pixbuf, padlock_pixbuf)
+ self.model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf,
+ gtk.gdk.Pixbuf)
+
+ self.model.set_sort_func(1, self._compareIters)
+ self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
+ self.modelfilter = self.model.filter_new()
+ self.modelfilter.set_visible_func(self._visible_func)
+ self.modelfilter.connect('row-has-child-toggled',
+ self.on_modelfilter_row_has_child_toggled)
+ self.tree.set_model(self.modelfilter)
+
for acct in gajim.connections:
self.add_account(acct)
self.add_account_contacts(acct)
# Recalculate column width for ellipsizing
self.tree.columns_autosize()
-
-
+
+
def select_contact(self, jid, account):
'''Select contact in roster. If contact is hidden but has events,
show him.'''
@@ -1297,21 +1274,21 @@ class RosterWindow:
##############################################################################
### Roster and Modelfilter handling
-##############################################################################
-
+##############################################################################
+
def _search_roster_func(self, model, column, key, titer):
if model[titer][C_NAME].decode('utf-8').lower().startswith(
gobject.markup_escape_text(key.lower())):
return False
return True
-
+
def refilter_shown_roster_items(self):
self.filtering = True
self.modelfilter.refilter()
- self.filtering = False
-
+ self.filtering = False
+
def contact_has_pending_roster_events(self, contact, account):
- ''' Return True if the contact or one if it resources has pending events'''
+ '''Return True if the contact or one if it resources has pending events'''
# jid has pending events
if gajim.events.get_nb_roster_events(account, contact.jid) > 0:
return True
@@ -1335,10 +1312,10 @@ class RosterWindow:
if contact.jid in gajim.to_be_removed[account]:
return True
return False
- return True
-
+ return True
+
def _visible_func(self, model, titer):
- '''Determine whether iter should be visible in the treeview'''
+ '''Determine whether iter should be visible in the treeview'''
type_ = model[titer][C_TYPE]
if not type_:
return False
@@ -1371,9 +1348,9 @@ class RosterWindow:
accounts = [account]
for _acc in accounts:
for contact in gajim.contacts.iter_contacts(_acc):
- # Is this contact in this group ?
- if group in contact.groups or (group == _('General') and not \
- contact.groups):
+ # Is this contact in this group ? (last part of if check if it's
+ # self contact)
+ if group in contact.groups:
if self.contact_is_visible(contact, _acc):
return True
return False
@@ -1488,7 +1465,7 @@ class RosterWindow:
return 0
################################################################################
-### FIXME: Methods that don't belong to roster window...
+### FIXME: Methods that don't belong to roster window...
### ... atleast not in there current form
################################################################################
@@ -1510,8 +1487,8 @@ class RosterWindow:
elif (time.time() - result[2]) > 2592000:
# ok, here we see that we have a message in unread messages table
- # that is older than a month. It is probably from someone not in our
- # roster for accounts we usually launch, so we will delete this id
+ # that is older than a month. It is probably from someone not in our
+ # roster for accounts we usually launch, so we will delete this id
# from unread message tables.
gajim.logger.set_read_messages([result[0]])
@@ -1574,7 +1551,8 @@ class RosterWindow:
# If we already have chat windows opened, update them with new contact
# instance
- for chat_control in gajim.interface.msg_win_mgr.get_chat_controls(ji, account):
+ for chat_control in gajim.interface.msg_win_mgr.get_chat_controls(ji,
+ account):
chat_control.contact = contact1
def _change_awn_icon_status(self, status):
@@ -1642,7 +1620,7 @@ class RosterWindow:
def connected_rooms(self, account):
if account in gajim.gc_connected[account].values():
return True
- return False
+ return False
def auto_join_bookmarks(self, account):
'''autojoin bookmarks that have 'auto join' on for this account'''
@@ -1655,14 +1633,15 @@ class RosterWindow:
minimize = bm['minimize'] in ('1', 'true')
gajim.interface.join_gc_room(account, jid, bm['nick'],
bm['password'], minimize = minimize)
-
+
def on_event_removed(self, event_list):
- '''Remove contacts on last events removed.
+ '''Remove contacts on last events removed.
Only performed if removal was requested before but the contact
still had pending events
'''
- contact_list = ((event.jid.split('/')[0], event.account) for event in event_list)
+ contact_list = ((event.jid.split('/')[0], event.account) for event in \
+ event_list)
for jid, account in contact_list:
self.draw_contact(jid, account)
@@ -1673,7 +1652,7 @@ class RosterWindow:
# Remove contact will delay removal if there are more events pending
self.remove_contact(jid, account, backend = True)
self.show_title()
-
+
def open_event(self, account, jid, event):
'''If an event was handled, return True, else return False'''
data = event.parameters
@@ -1707,7 +1686,7 @@ class RosterWindow:
dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
data[1])
gajim.events.remove_events(account, jid, event)
- return True
+ return True
return False
################################################################################
@@ -1720,7 +1699,7 @@ class RosterWindow:
else:
self.xml.get_widget('roster_vbox2').hide()
-
+
def show_tooltip(self, contact):
pointer = self.tree.get_pointer()
props = self.tree.get_path_at_pos(pointer[0], pointer[1])
@@ -1796,15 +1775,16 @@ class RosterWindow:
text = _('Enter your password for account %s') % account
if passwords.USER_HAS_GNOMEKEYRING and \
not passwords.USER_USES_GNOMEKEYRING:
- text += '\n' + _('Gnome Keyring is installed but not correctly started\
- (environment variable probably not correctly set)')
+ text += '\n' + _('Gnome Keyring is installed but not \
+ correctly started (environment variable probably not \
+ correctly set)')
w = dialogs.PassphraseDialog(_('Password Required'), text,
_('Save password'))
passphrase, save = w.run()
if passphrase == -1:
if child_iterA:
- self.model[child_iterA][0] = gajim.interface.jabber_state_images[
- '16']['offline']
+ self.model[child_iterA][0] = \
+ gajim.interface.jabber_state_images['16']['offline']
if gajim.interface.systray_enabled:
gajim.interface.systray.change_status('offline')
self.update_status_combobox()
@@ -1826,19 +1806,6 @@ class RosterWindow:
elif gajim.sleeper_state[account] not in ('autoaway', 'autoxa'):
gajim.sleeper_state[account] = 'off'
- if gajim.otr_module:
- # disconnect from ENCRYPTED OTR contexts when going
- # offline/invisible
- if status == 'offline' or status == 'invisible':
- ctx = gajim.connections[account].otr_userstates.context_root
- while ctx is not None:
- if ctx.msgstate == gajim.otr_module.OTRL_MSGSTATE_ENCRYPTED:
- disconnected = True
- gajim.otr_module.otrl_message_disconnect(gajim.connections[account].otr_userstates,
- (gajim.otr_ui_ops,
- {'account':account,'urgent':True}), ctx.accountname,
- ctx.protocol, ctx.username)
- ctx = ctx.next
if to:
gajim.connections[account].send_custom_status(status, txt, to)
else:
@@ -1889,10 +1856,11 @@ class RosterWindow:
win.redraw_tab(ctrl)
gajim.contacts.remove_contact(account, contact)
- elif contact.jid == gajim.get_jid_from_account(account) and show == 'offline':
- # Our SelfContact went offline. Remove him
+ elif contact.jid == gajim.get_jid_from_account(account) and \
+ show == 'offline':
+ # Our SelfContact went offline. Remove him from roster and contacts
self.remove_contact(contact.jid, account)
-
+ gajim.contacts.remove_contact(account, contact)
# print status in chat window and update status/GPG image
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
win = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
@@ -1944,16 +1912,18 @@ class RosterWindow:
for contact in [c for c in lcontact if (c.show != 'offline' or \
c.is_transport())]:
self.chg_contact_status(contact, 'offline', '', account)
- # Remove SelfContact from roster. It might be gone when we return
- self.remove_contact(gajim.get_jid_from_account(account), account)
-
+ # Remove SelfContact from roster and remove it.
+ # It might be gone when we return
+ self_jid = gajim.get_jid_from_account(account)
+ self.remove_contact(self_jid, account)
+ gajim.contacts.remove_jid(account, self_jid)
self.actions_menu_needs_rebuild = True
self.update_status_combobox()
# Force the rebuild now since the on_activates on the menu itself does
# not work with the os/x top level menubar
if sys.platform == 'darwin':
- self.make_menu(force = True)
-
+ self.make_menu(force = True)
+
def get_status_message(self, show):
if show in gajim.config.get_per('defaultstatusmsg'):
if gajim.config.get_per('defaultstatusmsg', show, 'enabled'):
@@ -1966,7 +1936,7 @@ class RosterWindow:
dlg.window.present() # show it on current workspace
message = dlg.run()
return message
-
+
def change_status(self, widget, account, status):
def change(account, status):
message = self.get_status_message(status)
@@ -1983,7 +1953,7 @@ class RosterWindow:
on_response_ok = (change, account, status))
else:
change(account, status)
-
+
def update_status_combobox(self):
# table to change index in connection.connected to index in combobox
table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
@@ -2021,13 +1991,13 @@ class RosterWindow:
prio = u.priority
show = u.show
return show
-
+
def on_message_window_delete(self, win_mgr, msg_win):
if gajim.config.get('one_message_window') == 'always_with_roster':
self.show_roster_vbox(True)
gtkgui_helpers.resize_window(self.window,
gajim.config.get('roster_width'),
- gajim.config.get('roster_height'))
+ gajim.config.get('roster_height'))
def close_all_from_dict(self, dic):
'''close all the windows in the given dictionary'''
@@ -2200,7 +2170,7 @@ class RosterWindow:
helpers.exec_command('python history_manager.py')
else: # Unix user
helpers.exec_command('python history_manager.py &')
-
+
def on_info(self, widget, contact, account):
'''Call vcard_information_window class to display contact's information'''
if gajim.connections[account].is_zeroconf:
@@ -2324,7 +2294,7 @@ class RosterWindow:
if not show:
show = 'online'
contact = gajim.contacts.create_contact(jid = jid,
- name = account, show = show,
+ name = account, groups = ['self_contact'], show = show,
status = roster.getStatus(jid+'/'+resource),
resource = resource,
priority = roster.getPriority(jid+'/'+resource))
@@ -2585,23 +2555,17 @@ class RosterWindow:
for g in helpers.special_groups:
if g in (new_text, old_text):
return
- # get all contacts in that group
- for jid in gajim.contacts.get_jid_list(account):
- contact = gajim.contacts.get_contact_with_highest_priority(
- account, jid)
- if old_text in contact.groups:
- # set them in the new one and remove it from the old
- contact.groups.remove(old_text)
- self.remove_contact(contact.jid, account)
- if new_text not in contact.groups:
- contact.groups.append(new_text)
- self.add_contact(contact.jid, account)
- gajim.connections[account].update_contact(contact.jid,
- contact.name, contact.groups)
- # If last removed iter was not visible, gajim.groups is not cleaned
- if gajim.groups[account].has_key(old_text):
- del gajim.groups[account][old_text]
- self.draw_group(new_text, account)
+ # update all contacts in the given group
+ if self.regroup:
+ accounts = gajim.connections.keys()
+ else:
+ accounts = [account,]
+ for acc in accounts:
+ for jid in gajim.contacts.get_jid_list(acc):
+ contact = gajim.contacts.get_first_contact_from_jid(acc, jid)
+ if old_text in contact.groups:
+ self.remove_contact_from_groups(jid, acc, [old_text,])
+ self.add_contact_to_groups(jid, acc, [new_text,])
def on_canceled():
if gajim.interface.instances.has_key('rename'):
@@ -2659,7 +2623,8 @@ class RosterWindow:
keyID = keyID[0]
keys[contact.jid] = keyID
- for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid, account):
+ for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid,
+ account):
ctrl.update_ui()
keys_str = ''
for jid in keys:
@@ -2791,7 +2756,7 @@ class RosterWindow:
if resource: # we MUST have one contact only in list_
contact_jid += '/' + resource
gajim.connections[room_account].send_invite(room_jid, contact_jid)
-
+
def on_all_groupchat_maximized(self, widget, group_list):
for (contact, account) in group_list:
self.on_groupchat_maximized(widget, contact.jid, account)
@@ -2799,14 +2764,14 @@ class RosterWindow:
def on_groupchat_maximized(self, widget, jid, account):
'''When a groupchat is maximised'''
ctrl = gajim.interface.minimized_controls[account][jid]
- mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account)
+ mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid,
+ ctrl.account)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
ctrl.account, ctrl.type_id)
ctrl.parent_win = mw
mw.new_tab(ctrl)
mw.set_active_tab(ctrl)
- mw.window.present()
del gajim.interface.minimized_controls[account][jid]
self.remove_groupchat(jid, account)
@@ -3051,7 +3016,7 @@ class RosterWindow:
jids
dialogs.ConfirmationDialog(pritext, sectext,
on_response_ok = (on_ok2, list_))
-
+
def on_send_custom_status(self, widget, contact_list, show, group=None):
'''send custom status'''
dlg = dialogs.ChangeStatusMessageDialog(show)
@@ -3090,7 +3055,8 @@ class RosterWindow:
return
status = model[active][2].decode('utf-8')
statuses_unified = helpers.statuses_unified() # status "desync'ed" or not
- if (active == 7 and statuses_unified) or (active == 9 and not statuses_unified):
+ if (active == 7 and statuses_unified) or (active == 9 and \
+ not statuses_unified):
# 'Change status message' selected:
# do not change show, just show change status dialog
status = model[self.previous_status_combobox_active][2].decode('utf-8')
@@ -3263,18 +3229,6 @@ class RosterWindow:
def on_profile_avatar_menuitem_activate(self, widget, account):
gajim.interface.edit_own_details(account)
- def play_tictactoe(self, widget, contact, account, resource=None):
- jid = contact.jid
-
- if resource is not None:
- jid = jid + u'/' + resource
-
- import tictactoe
-
- sess = gajim.connections[account].make_new_session(jid,
- cls=tictactoe.TicTacToeSession)
- sess.begin()
-
def on_execute_command(self, widget, contact, account, resource=None):
'''Execute command. Full JID needed; if it is other contact,
resource is necessary. Widget is unnecessary, only to be
@@ -3416,7 +3370,7 @@ class RosterWindow:
for group in gajim.groups[account]:
if gajim.groups[account][group]['expand']:
titer = self._get_group_iter(group, account)
- if titer:
+ if titer:
path = model.get_path(titer)
self.tree.expand_row(path, False)
elif type_ == 'contact':
@@ -3426,7 +3380,7 @@ class RosterWindow:
self.draw_contact(jid, account)
self._toggeling_row = False
-
+
def on_roster_treeview_row_collapsed(self, widget, titer, path):
'''When a row is collapsed change the icon of the arrow'''
self._toggeling_row = True
@@ -3438,7 +3392,7 @@ class RosterWindow:
accounts = gajim.connections.keys()
else:
accounts = [model[titer][C_ACCOUNT].decode('utf-8')]
-
+
type_ = model[titer][C_TYPE]
if type_ == 'group':
child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images[
@@ -3459,7 +3413,7 @@ class RosterWindow:
jid = model[titer][C_JID].decode('utf-8')
account = model[titer][C_ACCOUNT].decode('utf-8')
self.draw_contact(jid, account)
-
+
self._toggeling_row = False
def on_modelfilter_row_has_child_toggled(self, model, path, titer):
@@ -3470,14 +3424,14 @@ class RosterWindow:
if self._toggeling_row:
# Signal is emitted when we write to our model
return
-
+
type_ = model[titer][C_TYPE]
account = model[titer][C_ACCOUNT]
if not account:
return
account = account.decode('utf-8')
-
+
if type_ == 'contact':
child_iter = model.convert_iter_to_child_iter(titer)
if self.model.iter_has_child(child_iter):
@@ -3501,7 +3455,7 @@ class RosterWindow:
'''
# Selection can change when the model is filtered
# Only write to the model when filtering is finished!
-
+
# FIXME: When we are filtering our custom colors are somehow lost
model, list_of_paths = selection.get_selected_rows()
@@ -3520,7 +3474,7 @@ class RosterWindow:
jid = row[C_JID].decode('utf-8')
account = row[C_ACCOUNT].decode('utf-8')
self._last_selected_contact.append((jid, account))
- gobject.idle_add(self.draw_contact, jid, account, True)
+ gobject.idle_add(self.draw_contact, jid, account, True)
def on_service_disco_menuitem_activate(self, widget, account):
server_jid = gajim.config.get_per('accounts', account, 'hostname')
@@ -3542,8 +3496,8 @@ class RosterWindow:
if gajim.config.get('showoffline'):
# We need to filter twice to show groups with no contacts inside
# in the correct expand state
- self.refilter_shown_roster_items()
-
+ self.refilter_shown_roster_items()
+
def on_view_menu_activate(self, widget):
# Hide the show roster menu if we are not in the right windowing mode.
@@ -3556,8 +3510,8 @@ class RosterWindow:
# when num controls is 0 this menuitem is hidden, but still need to
# disable keybinding
if self.hpaned.get_child2() is not None:
- self.show_roster_vbox(widget.get_active())
-
+ self.show_roster_vbox(widget.get_active())
+
################################################################################
### Drag and Drop handling
################################################################################
@@ -3594,7 +3548,7 @@ class RosterWindow:
gajim.config.set('confirm_metacontacts', 'no')
else:
gajim.config.set('confirm_metacontacts', 'yes')
-
+
# We might have dropped on a metacontact.
# Remove it and readd later with updated family info
dest_family = gajim.contacts.get_metacontacts_family(account_dest,
@@ -3608,11 +3562,11 @@ class RosterWindow:
c_source.jid)
old_groups = c_source.groups
- # Remove old source contact(s)
+ # Remove old source contact(s)
if was_big_brother:
# We have got little brothers. Readd them all
self._remove_metacontact_family(old_family, account_source)
- else:
+ else:
# We are only a litle brother. Simply remove us from our big brother
if self._get_contact_iter(c_source.jid, account_source):
# When we have been in the group before.
@@ -3624,7 +3578,7 @@ class RosterWindow:
own_data['account'] = account_source
# Don't touch the rest of the family
old_family = [own_data]
-
+
# Apply new tag and update contact
for data in old_family:
if account_source != data['account'] and not self.regroup:
@@ -3644,7 +3598,7 @@ class RosterWindow:
new_family = gajim.contacts.get_metacontacts_family(account_source,
c_source.jid)
brothers = self._add_metacontact_family(new_family, account_source)
-
+
for c, acc in brothers:
self.draw_contact(c.jid, acc)
self.draw_avatar(c.jid, acc)
@@ -3652,7 +3606,7 @@ class RosterWindow:
old_groups.extend(c_dest.groups)
for g in old_groups:
self.draw_group(g, account_source)
-
+
self.draw_account(account_source)
context.finish(True, True, etime)
@@ -3669,16 +3623,17 @@ class RosterWindow:
_('Do _not ask me again'), on_response_ok = merge_contacts)
if not confirm_metacontacts: # First time we see this window
dlg.checkbutton.set_active(True)
-
- def on_drop_in_group(self, widget, account, c_source, grp_dest, is_big_brother,
- context, etime, grp_source = None):
+
+ def on_drop_in_group(self, widget, account, c_source, grp_dest,
+ is_big_brother, context, etime, grp_source = None):
if is_big_brother:
# add whole metacontact to new group
self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
- # remove afterwards so the contact is not moved to General in the meantime
+ # remove afterwards so the contact is not moved to General in the
+ # meantime
if grp_dest != grp_source:
- self.remove_contact_from_groups(c_source.jid, account, [grp_source,])
+ self.remove_contact_from_groups(c_source.jid, account, [grp_source])
else:
# Normal contact or little brother
family = gajim.contacts.get_metacontacts_family(account,
@@ -3703,13 +3658,15 @@ class RosterWindow:
else:
# Normal contact
self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
- # remove afterwards so the contact is not moved to General in the meantime
+ # remove afterwards so the contact is not moved to General in the
+ # meantime
if grp_dest != grp_source:
- self.remove_contact_from_groups(c_source.jid, account, [grp_source,])
+ self.remove_contact_from_groups(c_source.jid, account,
+ [grp_source])
if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
context.finish(True, True, etime)
-
+
def drag_drop(self, treeview, context, x, y, timestamp):
target_list = treeview.drag_dest_get_target_list()
@@ -3760,7 +3717,8 @@ class RosterWindow:
uri = data.strip()
uri_splitted = uri.split() # we may have more than one file dropped
try:
- uri_splitted.remove('\0') # This is always the last element in windows
+ # This is always the last element in windows
+ uri_splitted.remove('\0')
except ValueError:
pass
nb_uri = len(uri_splitted)
@@ -3831,7 +3789,8 @@ class RosterWindow:
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
+ # 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(
@@ -3887,12 +3846,12 @@ class RosterWindow:
self.on_drop_in_contact(treeview, account_source, c_source,
account_dest, c_dest, is_big_brother, context, etime)
return
-
+
################################################################################
### Everything about images and icons....
### Cleanup assigned to Jim++ :-)
################################################################################
-
+
def get_appropriate_state_images(self, jid, size = '16',
icon_name = 'online'):
'''check jid and return the appropriate state images dict for
@@ -3909,7 +3868,6 @@ class RosterWindow:
return self.transports_state_images[size][transport]
return gajim.interface.jabber_state_images[size]
-
def make_transport_state_images(self, transport):
'''initialise opened and closed 'transport' iconset dict'''
if gajim.config.get('use_transports_iconsets'):
@@ -3917,19 +3875,19 @@ class RosterWindow:
'16x16')
pixo, pixc = gtkgui_helpers.load_icons_meta()
self.transports_state_images['opened'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixo, transport = True)
+ gtkgui_helpers.load_iconset(folder, pixo, transport=True)
self.transports_state_images['closed'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixc, transport = True)
+ gtkgui_helpers.load_iconset(folder, pixc, transport=True)
folder = os.path.join(helpers.get_transport_path(transport), '32x32')
- self.transports_state_images['32'][transport] = gtkgui_helpers.load_iconset(
- folder, transport = True)
+ self.transports_state_images['32'][transport] = \
+ gtkgui_helpers.load_iconset(folder, transport=True)
folder = os.path.join(helpers.get_transport_path(transport), '16x16')
- self.transports_state_images['16'][transport] = gtkgui_helpers.load_iconset(
- folder, transport = True)
-
+ self.transports_state_images['16'][transport] = \
+ gtkgui_helpers.load_iconset(folder, transport=True)
+
def update_jabber_state_images(self):
# Update the roster
- self.draw_roster()
+ self.setup_and_draw_roster()
# Update the status combobox
model = self.status_combobox.get_model()
titer = model.get_iter_root()
@@ -3937,7 +3895,8 @@ class RosterWindow:
if model[titer][2] != '':
# If it's not change status message iter
# eg. if it has show parameter not ''
- model[titer][1] = gajim.interface.jabber_state_images['16'][model[titer][2]]
+ model[titer][1] = gajim.interface.jabber_state_images['16'][model[
+ titer][2]]
titer = model.iter_next(titer)
# Update the systray
if gajim.interface.systray_enabled:
@@ -3949,7 +3908,7 @@ class RosterWindow:
win.redraw_tab(ctrl)
self.update_status_combobox()
-
+
def set_account_status_icon(self, account):
status = gajim.connections[account].connected
child_iterA = self._get_account_iter(account, self.model)
@@ -3959,7 +3918,8 @@ class RosterWindow:
show = gajim.SHOW_LIST[status]
else: # accounts merged
show = helpers.get_global_show()
- self.model[child_iterA][C_IMG] = gajim.interface.jabber_state_images['16'][show]
+ self.model[child_iterA][C_IMG] = gajim.interface.jabber_state_images[
+ '16'][show]
################################################################################
### Style and theme related methods
@@ -4030,7 +3990,7 @@ class RosterWindow:
for contact in self._iter_contact_rows():
self.draw_contact(contact[C_JID].decode('utf-8'),
contact[C_ACCOUNT].decode('utf-8'))
-
+
def set_renderer_color(self, renderer, style, set_background = True):
'''set style for treeview cell, using PRELIGHT system color'''
if set_background:
@@ -4039,7 +3999,7 @@ class RosterWindow:
else:
fgcolor = self.tree.style.fg[style]
renderer.set_property('foreground-gdk', fgcolor)
-
+
def _iconCellDataFunc(self, column, renderer, model, titer, data = None):
'''When a row is added, set properties for icon renderer'''
theme = gajim.config.get('roster_theme')
@@ -4082,7 +4042,7 @@ class RosterWindow:
else:
renderer.set_property('xalign', 0.4)
renderer.set_property('width', 26)
-
+
def _nameCellDataFunc(self, column, renderer, model, titer, data = None):
'''When a row is added, set properties for name renderer'''
theme = gajim.config.get('roster_theme')
@@ -4202,18 +4162,19 @@ class RosterWindow:
renderer.set_property('xalign', 1) # align pixbuf to the right
else:
renderer.set_property('visible', False)
-
+
################################################################################
### Everything about building menus
### FIXME: We really need to make it simpler! 1465 lines are a few to much....
-################################################################################
+################################################################################
def make_menu(self, force = False):
'''create the main window\'s menus'''
if not force and not self.actions_menu_needs_rebuild:
return
new_chat_menuitem = self.xml.get_widget('new_chat_menuitem')
- single_message_menuitem = self.xml.get_widget('send_single_message_menuitem')
+ single_message_menuitem = self.xml.get_widget(
+ 'send_single_message_menuitem')
join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
muc_icon = gtkgui_helpers.load_icon('muc_active')
if muc_icon:
@@ -4476,10 +4437,13 @@ class RosterWindow:
advanced_sub_menu.show_all()
if sys.platform == 'darwin':
- syncmenu.takeover_menu(self.xml.get_widget('menubar'))
+ try:
+ syncmenu.takeover_menu(self.xml.get_widget('menubar'))
+ except NameError:
+ pass
+
+ self.actions_menu_needs_rebuild = False
- self.actions_menu_needs_rebuild = False
-
def build_account_menu(self, account):
# we have to create our own set of icons for the menu
# using self.jabber_status_images is poopoo
@@ -4570,8 +4534,8 @@ class RosterWindow:
else:
pep_config.set_sensitive(False)
pep_submenu.append(pep_config)
- pep_config.connect('activate', self.on_pep_services_menuitem_activate,
- account)
+ pep_config.connect('activate',
+ self.on_pep_services_menuitem_activate, account)
img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
gtk.ICON_SIZE_MENU)
pep_config.set_image(img)
@@ -4696,7 +4660,7 @@ class RosterWindow:
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
-
+
def make_group_menu(self, event, titer):
'''Make group's popup menu'''
model = self.modelfilter
@@ -4711,8 +4675,7 @@ class RosterWindow:
for jid in gajim.contacts.get_jid_list(account):
contact = gajim.contacts.get_contact_with_highest_priority(account,
jid)
- if group in contact.groups or (contact.groups == [] and group == \
- _('General')):
+ if group in contact.groups:
if contact.show not in ('offline', 'error'):
list_online.append((contact, account))
list_.append((contact, account))
@@ -4739,7 +4702,8 @@ class RosterWindow:
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'))
+ 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',
@@ -4757,10 +4721,12 @@ class RosterWindow:
menu.append(invite_menuitem)
# Send Custom Status
- send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
+ send_custom_status_menuitem = gtk.ImageMenuItem(
+ _('Send Cus_tom Status'))
# add a special img for this menuitem
if group in gajim.connections[account].blocked_groups:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
+ 'offline'))
send_custom_status_menuitem.set_sensitive(False)
else:
icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
@@ -4774,8 +4740,8 @@ class RosterWindow:
# icon MUST be different instance for every item
state_images = gtkgui_helpers.load_iconset(path)
status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
- status_menuitem.connect('activate', self.on_send_custom_status, list_,
- s, group)
+ 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)
@@ -4849,7 +4815,7 @@ class RosterWindow:
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)
+ menu.popup(None, None, None, event_button, event.time)
def make_contact_menu(self, event, titer):
'''Make contact\'s popup menu'''
@@ -4995,25 +4961,19 @@ class RosterWindow:
execute_command_menuitem = xml.get_widget(
'execute_command_menuitem')
- tictactoe_menuitem = xml.get_widget('tictactoe_menuitem')
- tictactoe_menuitem.connect('activate', self.play_tictactoe, contact,
- account, contact.resource)
-
# 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:
+ for group in contact.groups:
if group in gajim.connections[account].blocked_groups:
blocked = True
break
- if blocked:
+ if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
+ # Transport contact, send custom status unavailable
+ send_custom_status_menuitem.set_sensitive(False)
+ elif blocked:
send_custom_status_menuitem.set_image( \
gtkgui_helpers.load_icon('offline'))
send_custom_status_menuitem.set_sensitive(False)
@@ -5129,7 +5089,8 @@ class RosterWindow:
ask_auth_menuitem.connect('activate', self.req_sub, jid,
_('I would like to add you to my roster'), account,
contact.groups, contact.name)
- if contact.sub in ('to', 'none'):
+ if contact.sub in ('to', 'none') or gajim.get_transport_name_from_jid(
+ jid, use_config_setting=False):
revoke_auth_menuitem.set_sensitive(False)
else:
revoke_auth_menuitem.connect('activate', self.revoke_auth, jid,
@@ -5182,8 +5143,8 @@ class RosterWindow:
gtkgui_helpers.destroy_widget)
roster_contact_context_menu.show_all()
roster_contact_context_menu.popup(None, None, None, event_button,
- event.time)
-
+ event.time)
+
def make_multiple_contact_menu(self, event, iters):
'''Make group's popup menu'''
model = self.modelfilter
@@ -5280,7 +5241,7 @@ class RosterWindow:
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
-
+
def make_transport_menu(self, event, titer):
'''Make transport\'s popup menu'''
model = self.modelfilter
@@ -5306,7 +5267,8 @@ class RosterWindow:
send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
# add a special img for this menuitem
if blocked:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
+ 'offline'))
send_custom_status_menuitem.set_sensitive(False)
else:
if gajim.interface.status_sent_to_users.has_key(account) and \
@@ -5460,7 +5422,7 @@ class RosterWindow:
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
-
+
def build_resources_submenu(self, contacts, account, action, room_jid=None,
room_account=None):
''' Build a submenu with contact's resources.
@@ -5558,7 +5520,7 @@ class RosterWindow:
menuitem.connect('activate', self.on_invite_to_room, list_,
room_jid, account)
invite_to_submenu.append(menuitem)
-
+
def get_and_connect_advanced_menuitem_menu(self, account):
'''adds FOR ACCOUNT options'''
xml = gtkgui_helpers.get_glade('advanced_menuitem_menu.glade')
@@ -5605,7 +5567,7 @@ class RosterWindow:
advanced_menuitem_menu.show_all()
return advanced_menuitem_menu
-
+
def add_history_manager_menuitem(self, menu):
'''adds a seperator and History Manager menuitem BELOW for account
menuitems'''
@@ -5619,7 +5581,7 @@ 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.ImageMenuItem(_('_Join New Group Chat'))
@@ -5638,7 +5600,7 @@ class RosterWindow:
item.connect('activate', self.on_bookmark_menuitem_activate,
account, bookmark)
gc_sub_menu.append(item)
-
+
def set_actions_menu_needs_rebuild(self):
self.actions_menu_needs_rebuild = True
# Force the rebuild now since the on_activates on the menu itself does
@@ -5646,7 +5608,7 @@ class RosterWindow:
if sys.platform == 'darwin':
self.make_menu(force = True)
return
-
+
def show_appropriate_context_menu(self, event, iters):
# iters must be all of the same type
model = self.modelfilter
@@ -5686,8 +5648,10 @@ class RosterWindow:
self.show_appropriate_context_menu(event, iters)
return True
-
+
def setup_for_osx(self):
+ # This is broken
+ return
'''Massage the GTK menu so it will match up to the OS/X nib style menu
when passed to sync-menu and merged'''
main_menu = self.xml.get_widget('menubar')
@@ -5732,11 +5696,11 @@ class RosterWindow:
# Hide the GTK menubar itself and let the OS/X menubar do its thing
#self.xml.get_widget('menubar').hide()
return
-
+
################################################################################
-###
+###
################################################################################
-
+
def __init__(self):
self.filtering = False
self.xml = gtkgui_helpers.get_glade('roster_window.glade')
@@ -5784,20 +5748,6 @@ class RosterWindow:
self.popups_notification_height = 0
self.popup_notification_windows = []
- #(icon, name, type, jid, account, editable, avatar_pixbuf, padlock_pixbuf)
- self.model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf,
- gtk.gdk.Pixbuf)
-
- self.model.set_sort_func(1, self._compareIters)
- self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
- self.modelfilter = self.model.filter_new()
- self.modelfilter.set_visible_func(self._visible_func)
- self.modelfilter.connect('row-has-child-toggled',
- self.on_modelfilter_row_has_child_toggled)
- self.tree.set_model(self.modelfilter)
- # Workaroung: For strange reasons signal is behaving like row-changed
- self._toggeling_row = False
-
# Remove contact from roster when last event opened
# { (contact, account): { backend: boolean }
self.contacts_to_be_removed = {}
@@ -5834,8 +5784,8 @@ class RosterWindow:
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
uf_show = helpers.get_uf_show(show)
- liststore.append([uf_show, gajim.interface.jabber_state_images['16'][show],
- show, True])
+ liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
+ show], show, True])
# Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
liststore.append(['SEPARATOR', None, '', True])
@@ -5849,8 +5799,8 @@ class RosterWindow:
liststore.append(['SEPARATOR', None, '', True])
uf_show = helpers.get_uf_show('offline')
- liststore.append([uf_show, gajim.interface.jabber_state_images['16']['offline'],
- 'offline', True])
+ liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
+ 'offline'], 'offline', True])
status_combobox_items = ['online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'separator1', 'change_status_msg', 'separator2',
@@ -5927,8 +5877,8 @@ class RosterWindow:
# signals
self.TARGET_TYPE_URI_LIST = 80
- TARGETS = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_APP | 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,
@@ -5945,7 +5895,9 @@ class RosterWindow:
self.collapsed_rows = gajim.config.get('collapsed_rows').split('\t')
self.tooltip = tooltips.RosterTooltip()
- self.draw_roster()
+ # Workaroung: For strange reasons signal is behaving like row-changed
+ self._toggeling_row = False
+ self.setup_and_draw_roster()
for account in gajim.connections:
if gajim.config.get_per('accounts', account, 'publish_tune'):
diff --git a/src/session.py b/src/session.py
index 74666c048..d2823c89b 100644
--- a/src/session.py
+++ b/src/session.py
@@ -186,7 +186,6 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
if ctrl:
self.control = ctrl
self.control.set_session(self)
- self.control.parent_win.move_from_sessionless(self.control)
first = False
if pm:
@@ -317,8 +316,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
if popup:
if not self.control:
- self.control = gajim.interface.new_chat(self, contact,
- self.conn.name, resource=resource_for_chat)
+ self.control = gajim.interface.new_chat(contact,
+ self.conn.name, resource=resource_for_chat, session=self)
if len(gajim.events.get_events(self.conn.name, fjid)):
self.control.read_queue()
diff --git a/src/statusicon.py b/src/statusicon.py
index 8dfe843e3..54d5aec3e 100644
--- a/src/statusicon.py
+++ b/src/statusicon.py
@@ -27,7 +27,10 @@ from common import gajim
from common import helpers
if sys.platform == 'darwin':
- import osx
+ try:
+ import osx
+ except ImportError:
+ pass
class StatusIcon(systray.Systray):
'''Class for the notification area icon'''
@@ -69,7 +72,10 @@ class StatusIcon(systray.Systray):
self.status_icon.set_tooltip(text)
if gajim.events.get_nb_systray_events():
if sys.platform == 'darwin':
- osx.nsapp.requestUserAttention()
+ try:
+ osx.nsapp.requestUserAttention()
+ except NameError:
+ pass
state = 'event'
self.status_icon.set_blinking(True)
else:
diff --git a/src/systray.py b/src/systray.py
index b9df2c287..a2f0c10fb 100644
--- a/src/systray.py
+++ b/src/systray.py
@@ -109,7 +109,6 @@ class Systray:
if gajim.interface.msg_win_mgr.has_window(jid, account):
gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab(
jid, account)
- gajim.interface.msg_win_mgr.get_window(jid, account).window.present()
elif contact:
gajim.interface.new_chat(contact, account)
gajim.interface.msg_win_mgr.get_window(jid, account).set_active_tab(
diff --git a/src/tictactoe.py b/src/tictactoe.py
deleted file mode 100644
index 47db0ebf4..000000000
--- a/src/tictactoe.py
+++ /dev/null
@@ -1,427 +0,0 @@
-from common import stanza_session
-from common import xmpp
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-from gtk import gdk
-import cairo
-
-# implements <http://pidgin-games.sourceforge.net/xep/tictactoe.html#invite>
-
-games_ns = 'http://jabber.org/protocol/games'
-
-class InvalidMove(Exception):
- pass
-
-class TicTacToeSession(stanza_session.StanzaSession):
- # initiate a session
- def begin(self, rows = 3, cols = 3, role_s = 'x'):
- self.rows = rows
- self.cols = cols
-
- self.role_s = role_s
-
- self.strike = 3
-
- if self.role_s == 'x':
- self.role_o = 'o'
- else:
- self.role_o = 'x'
-
- self.send_invitation()
-
- self.next_move_id = 1
- self.received = self.wait_for_invite_response
-
- def send_invitation(self):
- msg = xmpp.Message()
-
- invite = msg.NT.invite
- invite.setNamespace(games_ns)
-
- game = invite.NT.game
- game.setAttr('var', games_ns + '/tictactoe')
-
- x = xmpp.DataForm(typ='submit')
-
- game.addChild(node=x)
-
- self.send(msg)
-
- def read_invitation(self, msg):
- invite = msg.getTag('invite', namespace=games_ns)
- game = invite.getTag('game')
- x = game.getTag('x', namespace='jabber:x:data')
-
- form = xmpp.DataForm(node=x)
-
- if form.getField('role'):
- self.role_o = form.getField('role').getValues()[0]
- else:
- self.role_o = 'x'
-
- if form.getField('rows'):
- self.rows = int(form.getField('rows').getValues()[0])
- else:
- self.rows = 3
-
- if form.getField('cols'):
- self.cols = int(form.getField('cols').getValues()[0])
- else:
- self.cols = 3
-
- # number in a row needed to win
- if form.getField('strike'):
- self.strike = int(form.getField('strike').getValues()[0])
- else:
- self.strike = 3
-
- # received an invitation
- def invited(self, msg):
- self.read_invitation(msg)
-
- # XXX prompt user
- # "accept, reject, ignore"
-
- # the number of the move about to be made
- self.next_move_id = 1
-
- # display the board
- self.board = TicTacToeBoard(self, self.rows, self.cols)
-
- # accept the invitation, join the game
- response = xmpp.Message()
-
- join = response.NT.join
- join.setNamespace(games_ns)
-
- self.send(response)
-
- if self.role_o == 'x':
- self.role_s = 'o'
-
- self.their_turn()
- else:
- self.role_s = 'x'
- self.role_o = 'o'
-
- self.our_turn()
-
- # just sent an invitation, expecting a reply
- def wait_for_invite_response(self, msg):
- if msg.getTag('join', namespace=games_ns):
- self.board = TicTacToeBoard(self, self.rows, self.cols)
-
- if self.role_s == 'x':
- self.our_turn()
- else:
- self.their_turn()
-
- elif msg.getTag('decline', namespace=games_ns):
- # XXX notify the user
-
- # XXX end session
- pass
-
- # silently ignores any received messages
- def ignore(self, msg):
- pass
-
- def game_over(self, msg):
- invite = msg.getTag('invite', namespace=games_ns)
-
- # ignore messages unless they're renewing the game
- if invite and invite.getAttr('type') == 'renew':
- self.invited(msg)
-
- def wait_for_move(self, msg):
- turn = msg.getTag('turn', namespace=games_ns)
- move = turn.getTag('move', namespace='http://jabber.org/protocol/games/tictactoe')
-
- row = int(move.getAttr('row'))
- col = int(move.getAttr('col'))
- id = int(move.getAttr('id'))
-
- if id != self.next_move_id:
- print 'unexpected move id, lost a move somewhere?'
- return
-
- try:
- self.board.mark(row, col, self.role_o)
- except InvalidMove, e:
- # received an invalid move, end the game.
-
- # XXX notify the user
- self.end_game('cheating')
- return
-
- # check win conditions
- if self.board.check_for_strike(self.role_o, row, col, self.strike):
- self.lost()
- elif self.board.full():
- self.drawn()
- else:
- self.next_move_id += 1
-
- self.our_turn()
-
- def is_my_turn(self):
- # XXX not great semantics
- return self.received == self.ignore
-
- def our_turn(self):
- # ignore messages until we've made our move
- self.received = self.ignore
- self.board.set_title('your turn')
-
- def their_turn(self):
- self.received = self.wait_for_move
- self.board.set_title('their turn')
-
- # called when the board receives input
- def move(self, row, col):
- try:
- self.board.mark(row, col, self.role_s)
- except InvalidMove, e:
- print 'you made an invalid move'
- return
-
- self.send_move(row, col)
-
- # check win conditions
- if self.board.check_for_strike(self.role_s, row, col, self.strike):
- self.won()
- elif self.board.full():
- self.drawn()
- else:
- self.next_move_id += 1
-
- self.their_turn()
-
- # sends a move message
- def send_move(self, row, column):
- msg = xmpp.Message()
- msg.setType('chat')
-
- turn = msg.NT.turn
- turn.setNamespace(games_ns)
-
- move = turn.NT.move
- move.setNamespace(games_ns+'/tictactoe')
-
- move.setAttr('row', str(row))
- move.setAttr('col', str(column))
- move.setAttr('id', str(self.next_move_id))
-
- self.send(msg)
-
- # sends a termination message and ends the game
- def end_game(self, reason):
- msg = xmpp.Message()
-
- terminate = msg.NT.terminate
- terminate.setNamespace(games_ns)
- terminate.setAttr('reason', reason)
-
- self.send(msg)
-
- self.received = self.game_over
-
- def won(self):
- self.end_game('won')
- self.board.won()
-
- def lost(self):
- self.end_game('lost')
- self.board.lost()
-
- def drawn(self):
- self.end_game('draw')
- self.board.drawn()
-
-class TicTacToeBoard:
- def __init__(self, session, rows, cols):
- self.session = session
-
- self.state = 'None'
-
- self.rows = rows
- self.cols = cols
-
- self.board = [ [None] * self.cols for r in xrange(self.rows) ]
-
- self.setup_window()
-
- # check if the last move (at row r and column c) won the game
- def check_for_strike(self, p, r, c, strike):
- # number in a row: up and down, left and right
- tallyI = 0
- tally_ = 0
-
- # number in a row: diagonal
- # (imagine L or F as two sides of a right triangle: L\ or F/)
- tallyL = 0
- tallyF = 0
-
- # convert real columns to internal columns
- r -= 1
- c -= 1
-
- for d in xrange(-strike, strike):
- r_in_range = 0 <= r+d < self.rows
- c_in_range = 0 <= c+d < self.cols
-
- # vertical check
- if r_in_range:
- tallyI = tallyI + 1
- if self.board[r+d][c] != p:
- tallyI = 0
-
- # horizontal check
- if c_in_range:
- tally_ = tally_ + 1
- if self.board[r][c+d] != p:
- tally_ = 0
-
- # diagonal checks
- if r_in_range and c_in_range:
- tallyL = tallyL + 1
- if self.board[r+d][c+d] != p:
- tallyL = 0
-
- if r_in_range and 0 <= c-d < self.cols:
- tallyF = tallyF + 1
- if self.board[r+d][c-d] != p:
- tallyF = 0
-
- if any([t == strike for t in (tallyL, tallyF, tallyI, tally_)]):
- return True
-
- return False
-
- # is the board full?
- def full(self):
- for r in xrange(self.rows):
- for c in xrange(self.cols):
- if self.board[r][c] == None:
- return False
-
- return True
-
- def setup_window(self):
- self.win = gtk.Window()
-
- self.title_prefix = 'tic-tac-toe with %s' % self.session.jid
- self.set_title()
-
- self.win.set_app_paintable(True)
-
- self.win.add_events(gdk.BUTTON_PRESS_MASK)
- self.win.connect('button-press-event', self.clicked)
- self.win.connect('expose-event', self.expose)
-
- self.win.show_all()
-
- def clicked(self, widget, event):
- if not self.session.is_my_turn():
- return
-
- (width, height) = widget.get_size()
-
- # convert click co-ordinates to row and column
-
- row_height = height // self.rows
- col_width = width // self.cols
-
- row = int(event.y // row_height) + 1
- column = int(event.x // col_width) + 1
-
- self.session.move(row, column)
-
- # this actually draws the board
- def expose(self, widget, event):
- win = widget.window
-
- cr = win.cairo_create()
-
- cr.set_source_rgb(1.0, 1.0, 1.0)
-
- cr.set_operator(cairo.OPERATOR_SOURCE)
- cr.paint()
-
- (width, height) = widget.get_size()
-
- row_height = height // self.rows
- col_width = width // self.cols
-
- for i in xrange(self.rows):
- for j in xrange(self.cols):
- if self.board[i][j] == 'x':
- self.draw_x(cr, i, j, row_height, col_width)
- elif self.board[i][j] == 'o':
- self.draw_o(cr, i, j, row_height, col_width)
-
- # XXX draw 'won', 'lost', 'draw'
-
- def draw_x(self, cr, row, col, row_height, col_width):
- cr.set_source_rgb(0, 0, 0)
-
- top = row_height * (row + 0.2)
- bottom = row_height * (row + 0.8)
-
- left = col_width * (col + 0.2)
- right = col_width * (col + 0.8)
-
- cr.set_line_width(row_height / 5)
-
- cr.move_to(left, top)
- cr.line_to(right, bottom)
-
- cr.move_to(right, top)
- cr.line_to(left, bottom)
-
- cr.stroke()
-
- def draw_o(self, cr, row, col, row_height, col_width):
- cr.set_source_rgb(0, 0, 0)
-
- x = col_width * (col + 0.5)
- y = row_height * (row + 0.5)
-
- cr.arc(x, y, row_height/4, 0, 2.0*3.2) # slightly further than 2*pi
-
- cr.set_line_width(row_height / 5)
- cr.stroke()
-
- # mark a move on the board
- def mark(self, row, column, player):
- if self.board[row-1][column-1]:
- raise InvalidMove
- else:
- self.board[row-1][column-1] = player
-
- self.win.queue_draw()
-
- def set_title(self, suffix = None):
- str = self.title_prefix
-
- if suffix:
- str += ': ' + suffix
-
- self.win.set_title(str)
-
- def won(self):
- self.state = 'won'
- self.set_title('you won!')
- self.win.queue_draw()
-
- def lost(self):
- self.state = 'lost'
- self.set_title('you lost.')
- self.win.queue_draw()
-
- def drawn(self):
- self.state = 'drawn'
- self.win.set_title(self.title_prefix + ': a draw.')
- self.win.queue_draw()
diff --git a/test/mocks.py b/test/mocks.py
new file mode 100644
index 000000000..213f79ddf
--- /dev/null
+++ b/test/mocks.py
@@ -0,0 +1,68 @@
+# gajim-specific mock objects
+from mock import Mock
+
+from common import gajim
+
+class MockConnection(Mock):
+ def __init__(self, name, *args):
+ Mock.__init__(self, *args)
+ self.name = name
+ gajim.connections[name] = self
+
+class MockWindow(Mock):
+ def __init__(self, *args):
+ Mock.__init__(self, *args)
+ self.window = Mock()
+
+class MockChatControl(Mock):
+ def __init__(self, *args):
+ Mock.__init__(self, *args)
+
+ self.parent_win = MockWindow({'get_active_control': self})
+ self.session = None
+
+ def set_session(self, sess):
+ self.session = sess
+
+ def __nonzero__(self):
+ return True
+
+ def __eq__(self, other):
+ return self is other
+
+class MockInterface(Mock):
+ def __init__(self, acct, *args):
+ Mock.__init__(self, *args)
+ self.msg_win_mgr = Mock()
+ self.roster = Mock()
+
+ self.remote_ctrl = None
+ self.minimized_controls = { acct: {} }
+
+class MockLogger(Mock):
+ def __init__(self):
+ Mock.__init__(self, {'write': None})
+
+class MockContact(Mock):
+ def __nonzero__(self):
+ return True
+
+import random
+
+class MockSession(Mock):
+ def __init__(self, conn, jid, thread_id, type):
+ Mock.__init__(self)
+
+ self.conn = conn
+ self.jid = jid
+ self.type = type
+ self.thread_id = thread_id
+
+ if not self.thread_id:
+ self.thread_id = '%0x' % random.randint(0, 10000)
+
+ def __repr__(self):
+ print '<MockSession %s>' % self.thread_id
+
+ def __nonzero__(self):
+ return True
diff --git a/test/test_sessions.py b/test/test_sessions.py
index e4276ff56..e47d3759a 100644
--- a/test/test_sessions.py
+++ b/test/test_sessions.py
@@ -10,10 +10,6 @@ sys.path.append(gajim_root + '/src')
# a temporary version of ~/.gajim for testing
configdir = gajim_root + '/test/tmp'
-import time
-
-from mock import Mock
-
# define _ for i18n
import __builtin__
__builtin__._ = lambda x: x
@@ -30,10 +26,15 @@ import common.configpaths
common.configpaths.gajimpaths.init(configdir)
common.configpaths.gajimpaths.init_profile()
+import time
+
# for some reason common.gajim needs to be imported before xmpppy?
from common import gajim
from common import xmpp
+from mock import Mock, expectParams
+from mocks import *
+
gajim.DATA_DIR = gajim_root + '/data'
from common.stanza_session import StanzaSession
@@ -41,16 +42,10 @@ from common.stanza_session import StanzaSession
# name to use for the test account
account_name = 'test'
-class MockConnection(Mock):
- def __init__(self, *args):
- Mock.__init__(self, *args)
- self.name = account_name
- gajim.connections[self.name] = self
-
class TestStanzaSession(unittest.TestCase):
def setUp(self):
self.jid = 'test@example.org/Gajim'
- self.conn = MockConnection({'send_stanza': None})
+ self.conn = MockConnection(account_name, {'send_stanza': None})
self.sess = StanzaSession(self.conn, self.jid, None, 'chat')
def test_generate_thread_id(self):
@@ -77,7 +72,7 @@ class TestStanzaSession(unittest.TestCase):
self.assertEqual(msg.getThread(), self.sess.thread_id)
- def test_terminate_without_sendng(self):
+ def test_terminate_without_sending(self):
# no termination is sent if no messages have been sent in the session
self.sess.terminate()
@@ -100,45 +95,7 @@ class TestStanzaSession(unittest.TestCase):
from session import ChatControlSession
-class MockWindow(Mock):
- def __init__(self, *args):
- Mock.__init__(self, *args)
- self.window = Mock()
-
-class MockChatControl(Mock):
- def __init__(self, *args):
- Mock.__init__(self, *args)
-
- self.parent_win = MockWindow({'get_active_control': self})
- self.session = None
-
- def set_session(self, sess):
- self.session = sess
-
- def __nonzero__(self):
- return True
-
- def __eq__(self, other):
- return self is other
-
-class MockInterface(Mock):
- def __init__(self, *args):
- Mock.__init__(self, *args)
- self.msg_win_mgr = Mock()
- self.roster = Mock()
-
- self.remote_ctrl = None
- self.minimized_controls = { account_name: {} }
-
-class MockLogger(Mock):
- def __init__(self):
- Mock.__init__(self, {'write': None})
-
-class MockContact(Mock):
- def __nonzero__(self):
- return True
-
-gajim.interface = MockInterface()
+gajim.interface = MockInterface(account_name)
gajim.contacts.add_account(account_name)
import notify
@@ -146,7 +103,7 @@ import notify
class TestChatControlSession(unittest.TestCase):
def setUp(self):
self.jid = 'test@example.org/Gajim'
- self.conn = MockConnection({'send_stanza': None})
+ self.conn = MockConnection(account_name, {'send_stanza': None})
self.sess = ChatControlSession(self.conn, self.jid, None)
gajim.logger = MockLogger()
@@ -217,6 +174,7 @@ class TestChatControlSession(unittest.TestCase):
def test_receive_already_has_control(self):
'''test receiving a message with a session already attached to a control'''
+
jid = 'bct@necronomicorp.com/Gajim'
msgtxt = 'testing one two three'
@@ -240,13 +198,16 @@ class TestChatControlSession(unittest.TestCase):
def test_received_orphaned_control(self):
'''test receiving a message when a control that doesn't have a session attached exists'''
- jid = 'bct@necronomicorp.com/Gajim'
+ jid = 'bct@necronomicorp.com'
+ fjid = jid + '/Gajim'
msgtxt = 'testing one two three'
ctrl = MockChatControl()
gajim.interface.msg_win_mgr = Mock({'get_sessionless_ctrl': ctrl})
+ gajim.interface.msg_win_mgr.mockSetExpectation('get_sessionless_ctrl',
+ expectParams(account_name, jid))
- self.receive_chat_msg(jid, msgtxt)
+ self.receive_chat_msg(fjid, msgtxt)
# message was logged
calls = gajim.logger.mockGetNamedCalls('write')