Author: js Date: 2008-12-24 00:42:49 +0100 (Wed, 24 Dec 2008) New Revision: 10895
Added: branches/osx_newbuildsys/src/common/resolver.py branches/osx_newbuildsys/src/common/xmpp/bosh.py branches/osx_newbuildsys/src/common/xmpp/proxy_connectors.py branches/osx_newbuildsys/src/common/xmpp/tls_nb.py branches/osx_newbuildsys/test/test_client_nb.py branches/osx_newbuildsys/test/test_nonblockingtcp.py branches/osx_newbuildsys/test/test_resolver.py branches/osx_newbuildsys/test/xmpp_mocks.py Removed: branches/osx_newbuildsys/src/common/nslookup.py branches/osx_newbuildsys/src/common/xmpp/auth.py branches/osx_newbuildsys/src/common/xmpp/browser.py branches/osx_newbuildsys/src/common/xmpp/commands.py branches/osx_newbuildsys/src/common/xmpp/dispatcher.py branches/osx_newbuildsys/src/common/xmpp/features.py branches/osx_newbuildsys/src/common/xmpp/filetransfer.py branches/osx_newbuildsys/src/common/xmpp/roster.py branches/osx_newbuildsys/src/common/xmpp/session.py branches/osx_newbuildsys/src/common/xmpp/transports.py Modified: branches/osx_newbuildsys/ChangeLog branches/osx_newbuildsys/README.html branches/osx_newbuildsys/data/glade/manage_proxies_window.glade branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/away.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/chat.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/dnd.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/not_in_roster.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/offline.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/online.png branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/xa.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/away.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/chat.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/dnd.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/not_in_roster.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/offline.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/online.png branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/xa.png branches/osx_newbuildsys/data/iconsets/transports/mrim/48x48/offline.png branches/osx_newbuildsys/data/iconsets/transports/mrim/48x48/online.png branches/osx_newbuildsys/debian/changelog branches/osx_newbuildsys/debian/control branches/osx_newbuildsys/debian/rules branches/osx_newbuildsys/gajim.nsi branches/osx_newbuildsys/po/de.po branches/osx_newbuildsys/po/es.po branches/osx_newbuildsys/setup_osx.py branches/osx_newbuildsys/setup_win32.py branches/osx_newbuildsys/src/chat_control.py branches/osx_newbuildsys/src/common/Makefile branches/osx_newbuildsys/src/common/config.py branches/osx_newbuildsys/src/common/connection.py branches/osx_newbuildsys/src/common/connection_handlers.py branches/osx_newbuildsys/src/common/stanza_session.py branches/osx_newbuildsys/src/common/xmpp/Makefile branches/osx_newbuildsys/src/common/xmpp/__init__.py branches/osx_newbuildsys/src/common/xmpp/auth_nb.py branches/osx_newbuildsys/src/common/xmpp/client.py branches/osx_newbuildsys/src/common/xmpp/client_nb.py branches/osx_newbuildsys/src/common/xmpp/debug.py branches/osx_newbuildsys/src/common/xmpp/dispatcher_nb.py branches/osx_newbuildsys/src/common/xmpp/features_nb.py branches/osx_newbuildsys/src/common/xmpp/idlequeue.py branches/osx_newbuildsys/src/common/xmpp/protocol.py branches/osx_newbuildsys/src/common/xmpp/roster_nb.py branches/osx_newbuildsys/src/common/xmpp/simplexml.py branches/osx_newbuildsys/src/common/xmpp/transports_nb.py branches/osx_newbuildsys/src/common/zeroconf/client_zeroconf.py branches/osx_newbuildsys/src/common/zeroconf/connection_zeroconf.py branches/osx_newbuildsys/src/config.py branches/osx_newbuildsys/src/dialogs.py branches/osx_newbuildsys/src/gajim.py branches/osx_newbuildsys/src/groupchat_control.py branches/osx_newbuildsys/src/notify.py branches/osx_newbuildsys/src/session.py branches/osx_newbuildsys/src/systray.py branches/osx_newbuildsys/test/test_dispatcher_nb.py Log: Merge trunk r10894.
Modified: branches/osx_newbuildsys/ChangeLog =================================================================== --- branches/osx_newbuildsys/ChangeLog 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/ChangeLog 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,3 +1,23 @@ +Gajim 0.12.1 (21 December 2008) + + * Fix filetransfer + * Updated german translation + * Fix click on notifications when text string is empty + * Improve systray popup menu + +Gajim 0.12 (17 December 2008) + + * Fix text rendering in notifications + * Fix forward unread messages under Windows + * Better sessions support + * Better auto-away support + * Fix banshee support + * Quodlibet support + * Fix GSSAPI authentification + * Fix IPV4 filetransfer on Windows Vista when IPV6 is used too + * Fix caps usage + * Fix end to end encryption autonegotiation + Gajim 0.12-beta1 (11 November 2008)
* SECURITY
Modified: branches/osx_newbuildsys/README.html =================================================================== --- branches/osx_newbuildsys/README.html 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/README.html 2008-12-23 23:42:49 UTC (rev 10895) @@ -40,6 +40,7 @@ <li>D-Bus running to have gajim-remote working</li> <li>python-dbus bindings</li> <li>python-sexy to have clickable URLs in chat windows</li> +<li>python-kerberos to use GSSAPI authentification. Note: version1.1 or higher is required</li> </ul>
<p>
Modified: branches/osx_newbuildsys/data/glade/manage_proxies_window.glade =================================================================== --- branches/osx_newbuildsys/data/glade/manage_proxies_window.glade 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/data/glade/manage_proxies_window.glade 2008-12-23 23:42:49 UTC (rev 10895) @@ -211,7 +211,8 @@ <widget class="GtkComboBox" id="proxytype_combobox"> <property name="visible">True</property> <property name="items" translatable="yes">HTTP Connect -SOCKS5</property> +SOCKS5 +BOSH</property> <property name="add_tearoffs">False</property> <property name="focus_on_click">True</property> <signal name="changed" handler="on_proxytype_combobox_changed" last_modification_time="Wed, 08 Jun 2005 17:45:26 GMT"/> @@ -234,6 +235,7 @@ <property name="max_length">0</property> <property name="text" translatable="yes"></property> <property name="has_frame">True</property> + <property name="invisible_char">●</property> <property name="activates_default">False</property> <signal name="changed" handler="on_proxyname_entry_changed" last_modification_time="Wed, 08 Jun 2005 17:43:44 GMT"/> </widget> @@ -304,7 +306,7 @@ <widget class="GtkTable" id="proxy_table"> <property name="visible">True</property> <property name="sensitive">False</property> - <property name="n_rows">5</property> + <property name="n_rows">8</property> <property name="n_columns">2</property> <property name="homogeneous">False</property> <property name="row_spacing">6</property> @@ -313,7 +315,7 @@ <child> <widget class="GtkLabel" id="label136"> <property name="visible">True</property> - <property name="label" translatable="yes">_Port:</property> + <property name="label" translatable="yes">Proxy _Port:</property> <property name="use_underline">True</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -332,8 +334,8 @@ <packing> <property name="left_attach">0</property> <property name="right_attach">1</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> <property name="x_options">fill</property> <property name="y_options"></property> </packing> @@ -348,14 +350,15 @@ <property name="max_length">0</property> <property name="text" translatable="yes"></property> <property name="has_frame">True</property> + <property name="invisible_char">●</property> <property name="activates_default">False</property> <signal name="changed" handler="on_proxyhost_entry_changed" last_modification_time="Wed, 08 Jun 2005 20:56:20 GMT"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">0</property> - <property name="bottom_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> <property name="y_options"></property> </packing> </child> @@ -369,14 +372,15 @@ <property name="max_length">0</property> <property name="text"></property> <property name="has_frame">True</property> + <property name="invisible_char">●</property> <property name="activates_default">False</property> <signal name="changed" handler="on_proxyport_entry_changed" last_modification_time="Wed, 08 Jun 2005 20:57:45 GMT"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> <property name="y_options"></property> </packing> </child> @@ -384,7 +388,7 @@ <child> <widget class="GtkLabel" id="label135"> <property name="visible">True</property> - <property name="label" translatable="yes">_Host:</property> + <property name="label" translatable="yes">Proxy _Host:</property> <property name="use_underline">True</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -403,8 +407,8 @@ <packing> <property name="left_attach">0</property> <property name="right_attach">1</property> - <property name="top_attach">0</property> - <property name="bottom_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> <property name="x_options">fill</property> <property name="y_options"></property> </packing> @@ -432,8 +436,8 @@ <packing> <property name="left_attach">0</property> <property name="right_attach">1</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> <property name="x_options">fill</property> <property name="y_options"></property> </packing> @@ -461,8 +465,8 @@ <packing> <property name="left_attach">0</property> <property name="right_attach">1</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> <property name="x_options">fill</property> <property name="y_options"></property> </packing> @@ -477,14 +481,15 @@ <property name="max_length">0</property> <property name="text" translatable="yes"></property> <property name="has_frame">True</property> + <property name="invisible_char">●</property> <property name="activates_default">False</property> <signal name="changed" handler="on_proxypass_entry_changed" last_modification_time="Wed, 08 Jun 2005 20:58:01 GMT"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">7</property> + <property name="bottom_attach">8</property> <property name="y_options"></property> </packing> </child> @@ -498,14 +503,15 @@ <property name="max_length">0</property> <property name="text" translatable="yes"></property> <property name="has_frame">True</property> + <property name="invisible_char">●</property> <property name="activates_default">False</property> <signal name="changed" handler="on_proxyuser_entry_changed" last_modification_time="Wed, 08 Jun 2005 20:57:53 GMT"/> </widget> <packing> <property name="left_attach">1</property> <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> + <property name="top_attach">6</property> + <property name="bottom_attach">7</property> <property name="y_options"></property> </packing> </child> @@ -514,7 +520,7 @@ <widget class="GtkCheckButton" id="useauth_checkbutton"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="label" translatable="yes">Use authentication</property> + <property name="label" translatable="yes">Use proxy authentication</property> <property name="use_underline">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> @@ -526,12 +532,137 @@ <packing> <property name="left_attach">0</property> <property name="right_attach">2</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="boshuri_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">_BOSH URL:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">proxyhost_entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="boshuri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">●</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_boshuri_entry_changed" last_modification_time="Thu, 24 Jul 2008 20:27:43 GMT"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="boshuseproxy_checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use HTTP proxy</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_boshuseproxy_checkbutton_toggled" last_modification_time="Thu, 24 Jul 2008 20:51:01 GMT"/> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> <property name="top_attach">2</property> <property name="bottom_attach">3</property> <property name="x_options">fill</property> <property name="y_options"></property> </packing> </child> + + <child> + <widget class="GtkLabel" id="boshport_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">B_OSH Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">proxyhost_entry</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="boshport_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">●</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_boshport_entry_changed" last_modification_time="Fri, 25 Jul 2008 21:27:58 GMT"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> </widget> </child> </widget>
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/away.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/chat.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/dnd.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/not_in_roster.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/offline.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/online.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/16x16/xa.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/away.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/chat.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/dnd.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/not_in_roster.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/offline.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/online.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/32x32/xa.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/48x48/offline.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/data/iconsets/transports/mrim/48x48/online.png =================================================================== (Binary files differ)
Modified: branches/osx_newbuildsys/debian/changelog =================================================================== --- branches/osx_newbuildsys/debian/changelog 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/debian/changelog 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,3 +1,22 @@ +gajim (0.12.1-1) unstable; urgency=low + + * New upstream release. + * Fix coming back from suspend. Closes: #500523 + * Fix menuitem shortcut. Closes: #440530 + * Warn user when logs database is brocken. Closes: #447889 + * Check server certificate using python-openssl. Closes: #450756 + * Don't pool to get played music from Banshee. Closes: #472862 + * Escape non-ascii password correcly. Closes: #495540 + * Fix URL recognition. Closes: #407806 + * Suggest python-kerberos for GSSAPI authentication. + * Suggest python-sexy for clickable links in chat banners. + * Recommends python-openssl instead of python-pyopenssl. Package name has + changed. + * Depends on libc6 for gtkspell.so module + * Build Gajim modules against python2.5 + + -- Yann Leboulanger asterix@lagaule.org Wed, 21 Dec 2008 14:40:16 +0100 + gajim (0.11.4-1) unstable; urgency=low
* New upstream release.
Modified: branches/osx_newbuildsys/debian/control =================================================================== --- branches/osx_newbuildsys/debian/control 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/debian/control 2008-12-23 23:42:49 UTC (rev 10895) @@ -13,9 +13,9 @@ Package: gajim Architecture: any XB-Python-Version: ${python:Versions} -Depends: python (>= 2.4), python-support (>= 0.7.1), python-glade2 (>= 2.8.0), python-gtk2 (>= 2.8.0), python-pysqlite2 | python (>= 2.5), dnsutils -Recommends: dbus, python-dbus, notification-daemon, python-gnupginterface, python-pyopenssl -Suggests: python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnome2-desktop (>= 2.16.0), gnome-keyring +Depends: python (>= 2.4), python-support (>= 0.7.1), python-glade2 (>= 2.8.0), python-gtk2 (>= 2.8.0), python-pysqlite2 | python (>= 2.5), dnsutils, libc6 +Recommends: dbus, python-dbus, notification-daemon, python-gnupginterface, python-openssl +Suggests: python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnome2-desktop (>= 2.16.0), gnome-keyring, python-sexy, python-kerberos (>= 1.1) Description: Jabber client written in PyGTK Gajim is a Jabber client. It has a tabbed user interface with normal chats, group chats, and has many features such as, TLS, GPG, SSL, multiple accounts,
Modified: branches/osx_newbuildsys/debian/rules =================================================================== --- branches/osx_newbuildsys/debian/rules 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/debian/rules 2008-12-23 23:42:49 UTC (rev 10895) @@ -7,7 +7,7 @@ include /usr/share/cdbs/1/rules/simple-patchsys.mk include /usr/share/cdbs/1/class/autotools.mk
-PYTHONVER = 2.4 +PYTHONVER = 2.5 DEB_CONFIGURE_EXTRA_FLAGS := --prefix=/usr DEB_MAKE_BUILD_TARGET := all PYTHON=python$(PYTHONVER) DEB_MAKE_INSTALL_TARGET = install PYTHON=python$(PYTHONVER) DESTDIR=$(DEB_DESTDIR)
Modified: branches/osx_newbuildsys/gajim.nsi =================================================================== --- branches/osx_newbuildsys/gajim.nsi 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/gajim.nsi 2008-12-23 23:42:49 UTC (rev 10895) @@ -189,7 +189,7 @@ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "DisplayName" "Gajim" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "UninstallString" "$INSTDIR\Uninstall.exe" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "DisplayIcon" "$INSTDIR\bin\Gajim.exe" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "DisplayVersion" "0.12-beta1" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "DisplayVersion" "0.12.1" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Gajim" "URLInfoAbout" "http://www.gajim.org/" WriteUninstaller "$INSTDIR\Uninstall.exe"
Modified: branches/osx_newbuildsys/po/de.po =================================================================== --- branches/osx_newbuildsys/po/de.po 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/po/de.po 2008-12-23 23:42:49 UTC (rev 10895) @@ -12,7 +12,7 @@ "Project-Id-Version: gajim 0.11\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2008-11-11 23:40+0100\n" -"PO-Revision-Date: 2008-12-05 23:01+0100\n" +"PO-Revision-Date: 2008-12-14 14:40+0100\n" "Last-Translator: Nico Gulden cilugnedon@gmx.de\n" "Language-Team: German translators@gajim.org\n" "MIME-Version: 1.0\n" @@ -126,7 +126,7 @@ #: ../data/glade/account_modification_window.glade.h:20 #: ../data/glade/accounts_window.glade.h:25 msgid "If checked, Gajim will remember the password for this account" -msgstr "Wenn aktiviert, wird Gajim das Passwort für dieses Konto speichern" +msgstr "Falls aktiviert, wird Gajim das Passwort für dieses Konto speichern"
#: ../data/glade/account_creation_wizard_window.glade.h:16 #: ../data/glade/account_modification_window.glade.h:26 @@ -312,34 +312,34 @@ #: ../data/glade/account_modification_window.glade.h:18 #: ../data/glade/accounts_window.glade.h:22 msgid "Hostname: " -msgstr "Hostname: " +msgstr "Servername: "
#: ../data/glade/account_modification_window.glade.h:19 #: ../data/glade/accounts_window.glade.h:23 msgid "If checked, Gajim will also broadcast some more IPs except from just your IP, so file transfer has higher chances of working." -msgstr "Wenn aktiviert, wird Gajim von mehr als nur Ihrer eigenen IP senden, wodurch die Wahrscheinlichkeit einer erfolgreicher Dateiübertragung erhöht wird." +msgstr "Falls aktiviert, wird Gajim von mehr als nur Ihrer eigenen IP senden, wodurch die Wahrscheinlichkeit einer erfolgreicher Dateiübertragung erhöht wird."
#: ../data/glade/account_modification_window.glade.h:21 #: ../data/glade/accounts_window.glade.h:26 msgid "If checked, Gajim will send keep-alive packets to prevent connection timeout which results in disconnection" -msgstr "Wenn aktiviert, sendet Gajim regelmäßig Keepalive-Pakete, um einen Verbindungsabbruch durch eine Zeitüberschreitung zu verhindern" +msgstr "Falls aktiviert, sendet Gajim regelmäßig Keepalive-Pakete, um einen Verbindungsabbruch durch eine Zeitüberschreitung zu verhindern"
#: ../data/glade/account_modification_window.glade.h:22 #: ../data/glade/zeroconf_properties_window.glade.h:8 msgid "If checked, Gajim will store the password in ~/.gajim/config with 'read' permission only for you" -msgstr "Wenn aktiviert, wird Gajim das Passwort mit ausschließlichen Lese-Rechten für Sie in ~/.gajim/config speichern" +msgstr "Falls aktiviert, wird Gajim das Passwort mit ausschließlichen Lese-Rechten für Sie in ~/.gajim/config speichern"
#: ../data/glade/account_modification_window.glade.h:23 #: ../data/glade/accounts_window.glade.h:27 #: ../data/glade/zeroconf_properties_window.glade.h:9 msgid "If checked, Gajim, when launched, will automatically connect to jabber using this account" -msgstr "Wenn aktiviert, wird Gajim während des Startvorgangs automatisch zu diesem Konto verbinden" +msgstr "Falls aktiviert, wird Gajim während des Startvorgangs automatisch zu diesem Konto verbinden"
#: ../data/glade/account_modification_window.glade.h:24 #: ../data/glade/accounts_window.glade.h:28 #: ../data/glade/zeroconf_properties_window.glade.h:10 msgid "If checked, any change to the global status (handled by the combobox at the bottom of the roster window) will change the status of this account accordingly" -msgstr "Wenn aktiviert, wird jede Änderung des globalen Status (verwaltet von der Combobox am Fuße der Kontaktliste) auch den Status dieses Kontakts entsprechend ändern" +msgstr "Falls aktiviert, wird jede Änderung des globalen Status (verwaltet von der Combobox am Fuße der Kontaktliste) auch den Status dieses Kontakts entsprechend ändern"
#: ../data/glade/account_modification_window.glade.h:25 #: ../data/glade/accounts_window.glade.h:31 @@ -396,7 +396,7 @@ #: ../data/glade/account_modification_window.glade.h:33 #: ../data/glade/accounts_window.glade.h:42 msgid "Priority is used in Jabber to determine who gets the events from the jabber server when two or more clients are connected using the same account; The client with the highest priority gets the events" -msgstr "Die Priorität wird von Jabber verwendet, um festzustellen, wer die Ereignisse des Jabber-Servers bekommt, wenn zwei oder mehr Clients mit dem selben Konto verbunden sind. Der Client mit der höchsten Priorität bekommt die Ereignisse" +msgstr "Die Priorität wird von Jabber verwendet, um festzustellen, wer die Ereignisse des Jabber-Servers bekommt, wenn zwei oder mehr Clients mit dem selben Konto verbunden sind. Der Client mit der höchsten Priorität bekommt die Ereignisse."
#: ../data/glade/account_modification_window.glade.h:34 #: ../data/glade/accounts_window.glade.h:43 @@ -479,7 +479,7 @@
#: ../data/glade/accounts_window.glade.h:11 msgid "Check this so Gajim will ask you before sending your password over an insecure connection." -msgstr "Wenn aktiviert, wird Gajim nachfragen, bevor das Passwort über eine unsichere Verbindung versendet wird." +msgstr "Falls aktiviert, wird Gajim nachfragen, bevor das Passwort über eine unsichere Verbindung versendet wird."
#: ../data/glade/accounts_window.glade.h:16 #: ../data/glade/zeroconf_information_window.glade.h:2 @@ -501,7 +501,7 @@
#: ../data/glade/accounts_window.glade.h:24 msgid "If checked, Gajim will get the password from a GPG agent like seahorse" -msgstr "Wenn aktiviert, wird Gajim das Passwort von einem GnuPG-Agent wie seahorse beziehen" +msgstr "Falls aktiviert, wird Gajim das Passwort von einem GnuPG-Agent wie seahorse beziehen"
#: ../data/glade/accounts_window.glade.h:29 #: ../data/glade/zeroconf_properties_window.glade.h:11 @@ -509,7 +509,7 @@ "If the default port that is used for incoming messages is unfitting for your setup you can select another one here.\n" "You might consider to change possible firewall settings." msgstr "" -"Wenn der Standardport, der für eingehende Nachrichten benutzt wird, nicht für Ihre Konfiguration geeignet ist, können sie hier einen anderen Port festlegen.\n" +"Falls der Standardport, der für eingehende Nachrichten benutzt wird, nicht für Ihre Konfiguration geeignet ist, können sie hier einen anderen Port festlegen.\n" "Möglicherweise ist es von Nöten, Ihre Firewall-Einstellungen zu ändern."
#: ../data/glade/accounts_window.glade.h:32 @@ -1107,7 +1107,7 @@
#: ../data/glade/filetransfers.glade.h:12 msgid "When a file transfer is complete show a popup notification" -msgstr "Zeige eine Benachrichtigung, wenn die Datei komplett übertragen wurde." +msgstr "Zeige eine Popup-Benachrichtigung, wenn die Datei komplett übertragen wurde."
#: ../data/glade/filetransfers.glade.h:13 #: ../src/filetransfers_window.py:793 @@ -1294,7 +1294,7 @@ "If you plan to do massive deletions, please make sure Gajim is not running. Generally avoid deletions with contacts you currently chat with." msgstr "" "<b>WARNUNG:</b>\n" -"Wenn Sie planen, viel zu löschen, stellen Sie sicher, dass Gajim nicht läuft. Vermeiden Sie generell Löschungen von Unterhaltungen mit Kontakten, mit denen Sie gerade chatten." +"Wenn Sie viel löschen, stellen Sie sicher, dass Gajim nicht läuft. Vermeiden Sie generell Löschungen von Unterhaltungen mit Kontakten, mit denen Sie gerade chatten."
#: ../data/glade/history_manager.glade.h:3 msgid "<big><b>Welcome to Gajim History Logs Manager</b></big>" @@ -1394,7 +1394,7 @@
#: ../data/glade/manage_bookmarks_window.glade.h:2 msgid "If checked, Gajim will join this group chat on startup" -msgstr "Wenn aktiviert, tritt Gajim dem Gruppenchat beim Programmstart bei" +msgstr "Falls aktiviert, tritt Gajim dem Gruppenchat beim Programmstart bei"
#: ../data/glade/manage_bookmarks_window.glade.h:3 msgid "Manage Bookmarks" @@ -1466,7 +1466,7 @@
#: ../data/glade/manage_proxies_window.glade.h:10 msgid "_Host:" -msgstr "_Server:" +msgstr "P_roxy:"
#: ../data/glade/message_window.glade.h:1 msgid "Add this contact to roster (Ctrl+D)" @@ -1554,7 +1554,7 @@
#: ../data/glade/preferences_window.glade.h:10 msgid "<b>GMail Options</b>" -msgstr "<b>Gmail-Optionen</b>" +msgstr "<b>Google Mail-Optionen</b>"
#: ../data/glade/preferences_window.glade.h:12 msgid "<b>Preset Status Messages</b>" @@ -1692,67 +1692,67 @@
#: ../data/glade/preferences_window.glade.h:51 msgid "If checked, Gajim will allow others to detect the operation system you are using" -msgstr "Wenn aktiviert, erlaubt Gajim anderen Ihr Betriebssystem festzustellen" +msgstr "Falls aktiviert, erlaubt Gajim anderen Ihr Betriebssystem festzustellen"
#: ../data/glade/preferences_window.glade.h:52 msgid "If checked, Gajim will also include information about the sender of the new emails" -msgstr "Wenn aktiviert, wird Gajim auch Informationen über den Absender von neuen E-Mails hinzufügen." +msgstr "Falls aktiviert, wird Gajim auch Informationen über den Absender von neuen E-Mails hinzufügen."
#: ../data/glade/preferences_window.glade.h:53 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." +msgstr "Falls aktiviert, wird Gajim den Status in Abwesend ändern, wenn der Computer nicht genutzt wird."
#: ../data/glade/preferences_window.glade.h:54 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." +msgstr "Falls aktiviert, wird Gajim den Status in Nicht Verfügbar ändern, wenn der Computer länger nicht genutzt wird."
#: ../data/glade/preferences_window.glade.h:55 msgid "If checked, Gajim will display avatars of contacts in roster window and in group chats" -msgstr "Wenn aktiviert, wird Gajim einen Avatar im Kontaktlistenfenster und in Gruppenchats einblenden" +msgstr "Falls aktiviert, wird Gajim einen Avatar im Kontaktlistenfenster und in Gruppenchats einblenden"
#: ../data/glade/preferences_window.glade.h:56 msgid "If checked, Gajim will display status messages of contacts under the contact name in roster window and in group chats" -msgstr "Wenn aktiviert, wird Gajim für jeden Kontakt unter dem Kontaktnamen in der Kontaktliste und im Gruppenchat-Fenster die Statusnachricht anzeigen" +msgstr "Falls aktiviert, wird Gajim für jeden Kontakt unter dem Kontaktnamen in der Kontaktliste und im Gruppenchat-Fenster die Statusnachricht anzeigen"
#: ../data/glade/preferences_window.glade.h:57 msgid "If checked, Gajim will display the activity of contacts in the roster window" -msgstr "Wenn aktiviert, wird Gajim die Aktivität der Kontakte im Kontaktlistenfenster anzeigen" +msgstr "Falls aktiviert, wird Gajim die Aktivität der Kontakte im Kontaktlistenfenster anzeigen"
#: ../data/glade/preferences_window.glade.h:58 msgid "If checked, Gajim will display the mood of contacts in the roster window" -msgstr "Wenn aktiviert, wird Gajim die Stimmung der Kontakte im Kontaktlistenfenster anzeigen" +msgstr "Falls aktiviert, wird Gajim die Stimmung der Kontakte im Kontaktlistenfenster anzeigen"
#: ../data/glade/preferences_window.glade.h:59 msgid "If checked, Gajim will display the tunes of contacts in the roster window" -msgstr "Wenn aktiviert, wird Gajim den Musiktitel der Kontakte im Kontaktlistenfenster anzeigen" +msgstr "Falls aktiviert, wird Gajim den Musiktitel der Kontakte im Kontaktlistenfenster anzeigen"
#: ../data/glade/preferences_window.glade.h:60 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" +msgstr "Falls aktiviert, wird Gajim Rechtschreibfehler im Chat-Eingabefenster hervorheben. Wenn keine Sprache im Eingabefeld definiert wurde (per Rechtsklick), wird die Standardsprache für diesen Kontakt oder Gruppenchat verwendet"
#: ../data/glade/preferences_window.glade.h:61 msgid "If checked, Gajim will ignore incoming events from unauthorized contacts. Use with caution, because it blocks all messages from any contact that is not in the roster" -msgstr "Wenn aktiviert, wird Gajim eingehende Ereignisse von unauthorisierten Kontakten ignorieren. Vorsicht bei der Benutzung, denn alle Nachrichten von Kontakten werden blockiert, die nicht in Ihrer Kontaktliste sind!" +msgstr "Falls aktiviert, wird Gajim eingehende Ereignisse von unauthorisierten Kontakten ignorieren. Vorsicht bei der Benutzung, denn alle Nachrichten von Kontakten werden blockiert, die nicht in Ihrer Kontaktliste sind"
#: ../data/glade/preferences_window.glade.h:62 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 ESession-Verschlüsselung der Chatpartner zustimmen muss; ansonsten wird nicht mitgeschnitten." +msgstr "Falls aktiviert, wird Gajim Logs von verschlüsselten Nachrichten aufzeichnen. Bitte bedenken Sie, dass bei der Benutzung von ESession-Verschlüsselung der Chatpartner zustimmen muss; ansonsten wird nicht mitgeschnitten."
#: ../data/glade/preferences_window.glade.h:63 msgid "If checked, Gajim will show a notification when a new e-mail is received via GMail" -msgstr "Wenn aktiviert, wird Gajim eine Benachrichtigung anzeigen, wenn eine neue E-Mail über Googlemail empfangen wurden" +msgstr "Falls aktiviert, wird Gajim eine Benachrichtigung anzeigen, wenn eine neue E-Mail über Google Mail empfangen wurden"
#: ../data/glade/preferences_window.glade.h:64 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, etc...)" -msgstr "Wenn Sie diese Option aktivieren wird Gajim Protokoll-spezifische Symbole verwenden (Ein Kontakt aus MSN wird z.B. das äquivalente MSN-Symbol für den Status "Anwesend", "Abwesend", "Beschäftigt", etc. haben)" +msgstr "Falls aktiviert, wird Gajim Protokoll-spezifische Symbole 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:65 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" +msgstr "Falls aktiviert, wird Gajim nicht nach einer Statusnachricht fragen. Die Standardnachricht wird anstelle benutzt"
#: ../data/glade/preferences_window.glade.h:66 msgid "If not disabled, Gajim will replace ascii smilies like ':)' with equivalent animated or static graphical emoticons" -msgstr "Wenn nicht deaktivert, wird Gajim ASCII-Smilies wie ':)' mit äquivalenten graphischen Emoticons ersetzen" +msgstr "Falls nicht deaktivert, wird Gajim ASCII-Smilies wie ":)" mit äquivalenten graphischen Emoticons ersetzen"
#: ../data/glade/preferences_window.glade.h:67 msgid "Log _encrypted chat session" @@ -1814,7 +1814,7 @@
#: ../data/glade/preferences_window.glade.h:82 msgid "Some messages may include rich content (formatting, colors etc). If checked, Gajim will just display the raw message text." -msgstr "Einige Nachrichten könnten formatierten Inhalt enthalten (Farben, Schriftarten etc.). Wenn aktiviert, wird Gajim nur den reinen Nachrichtentext anzeigen." +msgstr "Einige Nachrichten könnten formatierten Inhalt enthalten (Farben, Schriftarten etc.). Falls aktiviert, wird Gajim nur den reinen Nachrichtentext anzeigen."
#: ../data/glade/preferences_window.glade.h:83 msgid "Sort contacts by status" @@ -2072,7 +2072,7 @@ #: ../data/glade/profile_window.glade.h:9 #: ../data/glade/vcard_information_window.glade.h:11 msgid "<b>Extra Address:</b>" -msgstr "<b>Zusatz-Adresse:</b>" +msgstr "<b>Zusatzadresse:</b>"
#. Family Name #: ../data/glade/profile_window.glade.h:11 @@ -2115,7 +2115,7 @@ #: ../data/glade/profile_window.glade.h:20 #: ../data/glade/vcard_information_window.glade.h:23 msgid "<b>Phone No.:</b>" -msgstr "<b>Telefon-Nummer:</b>" +msgstr "<b>Telefonnummer:</b>"
#: ../data/glade/profile_window.glade.h:21 #: ../data/glade/vcard_information_window.glade.h:24 @@ -2711,7 +2711,7 @@ #. we talk about option description in advanced configuration editor #: ../src/advanced.py:180 msgid "(None)" -msgstr "(Kein)" +msgstr "(Keine)"
#: ../src/advanced.py:263 msgid "Hidden" @@ -3251,11 +3251,11 @@
#: ../src/common/config.py:125 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." +msgstr "Falls aktiviert, wird */_ nicht entfernt. So wird *abc* fett gedruckt, aber * * wird nicht entfernt."
#: ../src/common/config.py:128 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)" +msgstr "Verwendet ReStructuredText Text-Markup für HTML, und ASCII-Formatierung falls aktiviert. Informationen zur Syntax finden Sie unter http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html (Englisch). (Falls Sie das wünschen, installieren Sie docutils)"
#: ../src/common/config.py:137 msgid "Character to add after nickname when using nick completion (tab) in group chat." @@ -3267,7 +3267,7 @@
#: ../src/common/config.py:161 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)." +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:162 msgid "Characters that are printed before the nickname in conversations" @@ -3304,15 +3304,15 @@
#: ../src/common/config.py:179 msgid "If checked, Gajim can be controlled remotely using gajim-remote." -msgstr "Wenn aktiviert, kann Gajim von Außerhalb mittels gajim-remote kontrolliert werden." +msgstr "Falls aktiviert, kann Gajim von außerhalb mittels gajim-remote kontrolliert werden."
#: ../src/common/config.py:180 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." +msgstr "Falls 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:181 msgid "Sent chat state notifications. Can be one of all, composing_only, disabled." -msgstr "Senden von Chatstatus-Benachrichtigungen. Kann all, composing_only oder disabled sein." +msgstr "Senden von Chatstatus-Benachrichtigungen. Kann "all", "composing_only" oder "disabled" sein."
#: ../src/common/config.py:182 msgid "Displayed chat state notifications in chat windows. Can be one of all, composing_only, disabled." @@ -3336,7 +3336,7 @@
#: ../src/common/config.py:190 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 "Kommagetrennte Liste von Hosts die zusätzlich zum lokalen Host gesendet werden, für Datenübertragung im Fall von NAT/Port-Forwarding" +msgstr "Kommagetrennte Liste von FQHNs die zusätzlich zum lokalen Rechnernamen gesendet werden, für Datenübertragung im Fall von NAT/Port-Forwarding."
#: ../src/common/config.py:192 msgid "IEC standard says KiB = 1024 bytes, KB = 1000 bytes." @@ -3368,39 +3368,39 @@
#: ../src/common/config.py:213 msgid "Preview new messages in notification popup?" -msgstr "Vorschau neuer Nachrichten im Benachrichtigungs-Popup?" +msgstr "Vorschau neuer Nachrichten in der Popup-Benachrichtigung?"
#: ../src/common/config.py:218 msgid "A semicolon-separated list of words that will be highlighted in group chats." -msgstr "Eine mit Semilkolon separierte Liste von Worten, die in einem Gruppenchat hervorgehoben werden." +msgstr "Eine mit Semikolon separierte Liste von Worten, die in einem Gruppenchat hervorgehoben werden."
#: ../src/common/config.py:219 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 "Wenn aktiviert, wird Gajim beendet, wenn der X-Button des Fenstermanagers geklickt wird. Diese Option wird nur beachtet, wenn die Trayicon-Unterstützung aktiv ist." +msgstr "Falls aktiviert, wird Gajim beendet, wenn der X-Button des Fenstermanagers geklickt wird. Diese Option wird nur beachtet, wenn Trayicon verwendet wird."
#: ../src/common/config.py:220 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." +msgstr "Falls aktiviert, wird Gajim beim Starten überprüfen, ob Gajim der Standard-Jabber-Client ist."
#: ../src/common/config.py:221 msgid "If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated." -msgstr "Wenn aktiviert, wird Gajim ein Icon auf jedem Tab mit ungelesenen Nachrichten anzeigen. Abhängig vom Thema kann dieses Icon animiert sein." +msgstr "Falls aktiviert, wird Gajim ein Icon auf jedem Tab mit ungelesenen Nachrichten anzeigen. Abhängig vom Theme kann dieses Icon animiert sein."
#: ../src/common/config.py:222 msgid "If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window." -msgstr "Wenn aktiviert, wird Gajim für jeden Kontakt die Statusnachricht, falls nicht leer, unter dem jeweiligen Kontaktnamen in der Kontaktliste anzeigen." +msgstr "Falls aktiviert, wird Gajim für jeden Kontakt die Statusnachricht, sofern nicht leer, unter dem jeweiligen Kontaktnamen in der Kontaktliste anzeigen."
#: ../src/common/config.py:227 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." +msgstr "Definiere die Position des Avatars in der Kontaktliste. "left" (links) oder "right" (rechts) sind mögliche Optionen."
#: ../src/common/config.py:228 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 "Wenn aktiviert fragt Gajim nach einem Avatar für jeden Kontakt, der beim letzten Mal keinen hatte oder einen veralteten hat." +msgstr "Falls aktiviert, fragt Gajim nach einem Avatar für jeden Kontakt, der beim letzten Mal keinen hatte oder dessen gespeicherter Avatar zu alt ist."
#: ../src/common/config.py:229 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 "Wenn deaktiviert, werden Sie keine Statuszeile im Chat sehen, wenn ein Kontakt seinen Status und/oder seine Statusnachricht ändert." +msgstr "Falls deaktiviert, werden Sie keine Statuszeile im Chat sehen, wenn ein Kontakt seinen Status und/oder seine Statusnachricht ändert."
#: ../src/common/config.py:230 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." @@ -3408,15 +3408,15 @@
#: ../src/common/config.py:232 msgid "Background color of contacts when they just signed in." -msgstr "Hintergrundfarbe von Kontakten, wenn sie sich gerade anmeldeten." +msgstr "Hintergrundfarbe von Kontakten, wenn sie sich gerade angemeldet haben."
#: ../src/common/config.py:233 msgid "Background color of contacts when they just signed out." -msgstr "Hintergrundfarbe von Kontakten, wenn sie sich gerade abmeldeten." +msgstr "Hintergrundfarbe von Kontakten, wenn sie sich gerade abgemeldet haben."
#: ../src/common/config.py:235 msgid "If True, restored messages will use a smaller font than the default one." -msgstr "Wenn aktiv, werden wiederhergestellte Nachrichten mit einer kleineren Schrift als der Standard dargestellt." +msgstr "Falls aktiviert, werden wiederhergestellte Nachrichten mit einer kleineren Schrift als der Standard dargestellt."
#: ../src/common/config.py:236 msgid "Don't show avatar for the transport itself." @@ -3428,7 +3428,7 @@
#: ../src/common/config.py:238 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) " +msgstr "Falls aktiviert und die installierten GTK+ und PyGTK Versionen wenigstens 2.8 sind, lasse das Fenster bei neuen Ereignissen aufblitzen (Standardverhalten in den meisten Window-Managern), wenn anstehende Ereignisse vorliegen."
#: ../src/common/config.py:240 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." @@ -3453,11 +3453,11 @@
#: ../src/common/config.py:244 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." +msgstr "Falls deaktiviert, werden Sie den Avatar nicht mehr im Chatfenster sehen."
#: ../src/common/config.py:245 msgid "If True, pressing the escape key closes a tab/window." -msgstr "Wenn aktiviert, schließt die Escape-Taste ein Tab/Fenster." +msgstr "Falls aktiviert, schließt die Escape-Taste ein Tab/Fenster."
#: ../src/common/config.py:246 msgid "Hides the buttons in chat windows." @@ -3477,7 +3477,7 @@
#: ../src/common/config.py:250 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." +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:251 msgid "Indentation when using merge consecutive nickname." @@ -3501,15 +3501,15 @@
#: ../src/common/config.py:256 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." +msgstr "Falls 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:257 msgid "If True, Gajim will use Gnome Keyring (if available) to store account passwords." -msgstr "Wenn aktiviert, wird Gnome Keyring (insofern verfügbar) verwendet, um die Passwörter der Konten zu speichern." +msgstr "Falls aktiviert, wird Gnome Keyring (falls verfügbar) verwendet, um die Passwörter der Konten zu speichern."
#: ../src/common/config.py:258 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." +msgstr "Falls aktiviert, wird Gajim die Anzahl der angemeldeten und gesamten Kontakte in den Konto- und Gruppenreihen anzeigen."
#: ../src/common/config.py:259 msgid "Can be empty, 'chat' or 'normal'. If not empty, treat all incoming messages as if they were of this type" @@ -3517,15 +3517,15 @@
#: ../src/common/config.py:260 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." +msgstr "Falls 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:261 msgid "If True, Gajim will convert string between $$ and $$ to an image using dvips and convert before insterting it in chat window." -msgstr "Wenn 'true', wird Gajim eine Zeichenkette zwischen $$ und $$ mittels dvips vor dem Einfügen in ein Chat-Fenster in ein Bild umwandeln." +msgstr "Falls aktiviert, wird Gajim eine Zeichenkette zwischen $$ und $$ mittels dvips vor dem Einfügen in ein Chat-Fenster in ein Bild umwandeln."
#: ../src/common/config.py:262 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." +msgstr "Benötigte Inaktivitäts-Zeit bevor das "Status ändern"-Fenster geschlossen wird."
#: ../src/common/config.py:263 msgid "Maximum number of lines that are printed in conversations. Oldest lines are cleared." @@ -3533,7 +3533,7 @@
#: ../src/common/config.py:264 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." +msgstr "Falls aktiviert, werden Benachrichtigungsfenster vom Benachrichtigungsdienst an das Systray-Icon angehängt."
#: ../src/common/config.py:265 msgid "Choose interval between 2 checks of idleness." @@ -3557,11 +3557,11 @@
#: ../src/common/config.py:287 msgid "If enabled, restore the last status that was used." -msgstr "Wenn aktiviert, wird die letzte Statusmeldung wiederhergestellt." +msgstr "Falls aktiviert, wird die letzte Statusmeldung wiederhergestellt."
#: ../src/common/config.py:292 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." +msgstr "Falls deaktiviert, wird die Anwesenheit nicht mit einem GPG-Schlüssel signiert, wenn GPG konfiguriert ist."
#: ../src/common/config.py:294 msgid "Enable ESessions encryption for this account." @@ -3598,7 +3598,7 @@
#: ../src/common/config.py:320 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." +msgstr "Falls aktiviert, wird Gajim Ihre IP und die in der file_transfer_proxies Option definierten Proxies für den Datentransfer verwenden."
#: ../src/common/config.py:334 msgid "Answer to receipt requests" @@ -3752,7 +3752,7 @@ #: ../src/common/connection_handlers.py:233 #: ../src/common/zeroconf/connection_handlers_zeroconf.py:93 msgid "Wrong host" -msgstr "Falscher Host" +msgstr "Falscher Server"
#: ../src/common/connection_handlers.py:234 msgid "Invalid local address? :-O" @@ -3996,15 +3996,15 @@
#: ../src/common/connection.py:93 msgid "Subject issuer mismatch" -msgstr "" +msgstr "Subject issuer mismatch"
#: ../src/common/connection.py:94 msgid "Authority and subject key identifier mismatch" -msgstr "" +msgstr "Authority and subject key identifier mismatch"
#: ../src/common/connection.py:95 msgid "Authority and issuer serial number mismatch" -msgstr "" +msgstr "Authority and issuer serial number mismatch"
#: ../src/common/connection.py:96 msgid "Key usage does not include certificate signing" @@ -4382,7 +4382,7 @@
#: ../src/common/helpers.py:169 msgid "Invalid character in hostname." -msgstr "Ungültiges Zeichen in Hostname." +msgstr "Ungültiges Zeichen im Rechnernamen."
#: ../src/common/helpers.py:176 msgid "Invalid character in resource." @@ -5076,11 +5076,11 @@
#: ../src/common/pep.py:143 msgid "Working out" -msgstr "Ausarbeiten" +msgstr "Trainieren"
#: ../src/common/pep.py:144 msgid "Grooming" -msgstr "Waschen" +msgstr "Körperpflege"
#: ../src/common/pep.py:145 msgid "At the Spa" @@ -5100,7 +5100,7 @@
#: ../src/common/pep.py:149 msgid "Taking a Bath" -msgstr "Baden" +msgstr "Ein Bad nehmen"
#: ../src/common/pep.py:150 msgid "Taking a Shower" @@ -5120,7 +5120,7 @@
#: ../src/common/pep.py:155 msgid "Hiding" -msgstr "Verstecken" +msgstr "Untertauchen"
#: ../src/common/pep.py:156 msgid "On Vacation" @@ -5275,12 +5275,12 @@
#: ../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." +msgstr "Verbindung zum Server 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." +msgstr "Der Server %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." @@ -5421,7 +5421,7 @@
#: ../src/config.py:1060 msgid "Group Chat Message Highlight" -msgstr "Gruppenchat Nachrichten-Hervorhebung" +msgstr "Gruppenchat-Nachrichten-Hervorhebung"
#: ../src/config.py:1061 msgid "Group Chat Message Received" @@ -5429,7 +5429,7 @@
#: ../src/config.py:1062 msgid "GMail Email Received" -msgstr "E-Mail über Googlemail empfangen" +msgstr "E-Mail über Google Mail empfangen"
#. Name column #: ../src/config.py:1319 @@ -6813,7 +6813,7 @@
#: ../src/features_window.py:88 msgid "Passive popups notifying for new events." -msgstr "Popups informieren über neue Ereignisse." +msgstr "Passive Popups informieren über neue Ereignisse."
#: ../src/features_window.py:89 msgid "Requires python-notify or instead python-dbus in conjunction with notification-daemon."
Modified: branches/osx_newbuildsys/po/es.po =================================================================== --- branches/osx_newbuildsys/po/es.po 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/po/es.po 2008-12-23 23:42:49 UTC (rev 10895) @@ -8,9 +8,9 @@ msgstr "" "Project-Id-Version: gajim 2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-11-11 23:40+0100\n" +"POT-Creation-Date: 2008-12-17 10:29+0100\n" "PO-Revision-Date: 2005-05-21 20:03+0100\n" -"Last-Translator: Membris Khan membriskhan@gmail.com\n" +"Last-Translator: Membris Khan frank@giingo.org\n" "Language-Team: none\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -48,24 +48,21 @@ msgstr "_Ejecutar comando..."
#: ../data/glade/account_context_menu.glade.h:5 -#, fuzzy msgid "_Modify Account" -msgstr "Modificar cuenta" +msgstr "_Modificar cuenta"
#: ../data/glade/account_context_menu.glade.h:6 msgid "_Open Gmail Inbox" msgstr "_Abrir bandeja de entrada de GMail"
#: ../data/glade/account_context_menu.glade.h:7 -#, fuzzy msgid "_Personal Events" -msgstr "Información personal" +msgstr "Eventos _personales"
#: ../data/glade/account_context_menu.glade.h:8 #: ../data/glade/roster_window.glade.h:24 -#, fuzzy msgid "_Start Chat..." -msgstr "Iniciar conversación" +msgstr "Iniciar conver_sación"
#: ../data/glade/account_context_menu.glade.h:9 #: ../data/glade/zeroconf_context_menu.glade.h:2 @@ -73,13 +70,12 @@ msgstr "_Estado"
#: ../data/glade/account_creation_wizard_window.glade.h:1 -#, fuzzy msgid "" "<b>Connecting to server</b>\n" "\n" "Please wait..." msgstr "" -"<b>Se está creando La cuenta</b>\n" +"<b>Conectando al servidor</b>\n" "\n" "Por favor espera..."
@@ -93,115 +89,103 @@
#: ../data/glade/account_creation_wizard_window.glade.h:6 msgid "<b>Please select a server</b>" -msgstr "" +msgstr "<b>Por favor, selecciona un servidor</b>"
#: ../data/glade/account_creation_wizard_window.glade.h:7 msgid "" "Add this certificate to the list of trusted certificates.\n" "SHA1 fingerprint of the certificate:\n" msgstr "" +"Añadir este certificado a la lista de certificados de confianza.\n" +"Huella SHA1 del certificado:\n"
#: ../data/glade/account_creation_wizard_window.glade.h:10 -msgid "Click to see features (like MSN, ICQ transports) of jabber servers" -msgstr "" -"Click para ver características (como transportes de MSN e ICQ) de los " -"servidores de Jabber" - -#: ../data/glade/account_creation_wizard_window.glade.h:11 msgid "Connect when I press Finish" msgstr "Conectar cuando presione Finalizar"
-#: ../data/glade/account_creation_wizard_window.glade.h:12 +#: ../data/glade/account_creation_wizard_window.glade.h:11 msgid "Gajim: Account Creation Wizard" msgstr "Gajim: Asistente para crear una cuenta"
-#: ../data/glade/account_creation_wizard_window.glade.h:13 -#, fuzzy +#: ../data/glade/account_creation_wizard_window.glade.h:12 msgid "I already have an account I want to _use" msgstr "Ya tengo una cuenta y quiero usarla"
-#: ../data/glade/account_creation_wizard_window.glade.h:14 +#: ../data/glade/account_creation_wizard_window.glade.h:13 msgid "I want to _register for a new account" msgstr "Quiero _registrar una nueva cuenta de Jabber"
-#: ../data/glade/account_creation_wizard_window.glade.h:15 +#: ../data/glade/account_creation_wizard_window.glade.h:14 #: ../data/glade/account_modification_window.glade.h:20 #: ../data/glade/accounts_window.glade.h:25 msgid "If checked, Gajim will remember the password for this account" msgstr "Si está marcado, Gajim recordará la contraseña para esta cuenta"
-#: ../data/glade/account_creation_wizard_window.glade.h:16 +#: ../data/glade/account_creation_wizard_window.glade.h:15 #: ../data/glade/account_modification_window.glade.h:26 #: ../data/glade/accounts_window.glade.h:34 msgid "Manage..." msgstr "Gestionar..."
-#: ../data/glade/account_creation_wizard_window.glade.h:17 -#, fuzzy +#: ../data/glade/account_creation_wizard_window.glade.h:16 msgid "Prox_y:" -msgstr "Proxy" +msgstr "Pro_xy"
-#: ../data/glade/account_creation_wizard_window.glade.h:18 +#: ../data/glade/account_creation_wizard_window.glade.h:17 #: ../data/glade/account_modification_window.glade.h:40 #: ../data/glade/accounts_window.glade.h:48 msgid "Save pass_word" msgstr "_Guardar contraseña"
-#: ../data/glade/account_creation_wizard_window.glade.h:19 -msgid "Servers Features" -msgstr "Características de los servidores" - -#: ../data/glade/account_creation_wizard_window.glade.h:20 +#: ../data/glade/account_creation_wizard_window.glade.h:18 msgid "Set my profile when I connect" msgstr "Definir mi perfil cuando me conecte"
-#: ../data/glade/account_creation_wizard_window.glade.h:21 +#: ../data/glade/account_creation_wizard_window.glade.h:19 #: ../data/glade/account_modification_window.glade.h:45 #: ../data/glade/accounts_window.glade.h:53 msgid "Use custom hostname/port" msgstr "Usar nombre de host y puerto personalizados"
-#: ../data/glade/account_creation_wizard_window.glade.h:22 +#: ../data/glade/account_creation_wizard_window.glade.h:20 msgid "" "You need to have an account in order to connect\n" "to the Jabber network." msgstr "Debes tener una cuenta antes de conectar a la red Jabber"
-#: ../data/glade/account_creation_wizard_window.glade.h:24 +#: ../data/glade/account_creation_wizard_window.glade.h:22 msgid "Your JID:" msgstr "Tu JID"
-#: ../data/glade/account_creation_wizard_window.glade.h:25 +#: ../data/glade/account_creation_wizard_window.glade.h:23 #: ../data/glade/roster_window.glade.h:14 msgid "_Advanced" msgstr "_Avanzado"
-#: ../data/glade/account_creation_wizard_window.glade.h:26 +#: ../data/glade/account_creation_wizard_window.glade.h:24 msgid "_Finish" msgstr "_Finalizar"
-#: ../data/glade/account_creation_wizard_window.glade.h:27 -#, fuzzy +#: ../data/glade/account_creation_wizard_window.glade.h:25 msgid "_Hostname:" msgstr "Nombre del host:"
-#: ../data/glade/account_creation_wizard_window.glade.h:28 +#: ../data/glade/account_creation_wizard_window.glade.h:26 #: ../data/glade/account_modification_window.glade.h:50 #: ../data/glade/accounts_window.glade.h:58 msgid "_Password:" msgstr "C_ontraseña:"
-#: ../data/glade/account_creation_wizard_window.glade.h:29 -#: ../data/glade/manage_proxies_window.glade.h:11 +#: ../data/glade/account_creation_wizard_window.glade.h:27 msgid "_Port:" msgstr "_Puerto:"
-#: ../data/glade/account_creation_wizard_window.glade.h:30 +#: ../data/glade/account_creation_wizard_window.glade.h:28 msgid "_Server:" msgstr "_Servidor:"
-#: ../data/glade/account_creation_wizard_window.glade.h:31 -#: ../data/glade/manage_proxies_window.glade.h:12 +#: ../data/glade/account_creation_wizard_window.glade.h:29 +#: ../data/glade/manage_proxies_window.glade.h:16 msgid "_Username:" msgstr "_Nombre"
@@ -234,13 +218,11 @@
#: ../data/glade/account_modification_window.glade.h:6 #: ../data/glade/accounts_window.glade.h:7 -#, fuzzy msgid "Administration operations" -msgstr "Lista de administradores" +msgstr "Operaciones de administración"
#: ../data/glade/account_modification_window.glade.h:7 #: ../data/glade/accounts_window.glade.h:8 -#, fuzzy msgid "Auto-reconnect when connection is lost" msgstr "Autoreconectar si se pierde la conexión"
@@ -281,6 +263,8 @@ #: ../data/glade/accounts_window.glade.h:14 msgid "Click to request authorization to all contacts of another account" msgstr "" +"Haz click para solicitar una autorización a todos los contactos para una " +"nueva cuenta"
#: ../data/glade/account_modification_window.glade.h:14 #: ../data/glade/accounts_window.glade.h:15 @@ -295,9 +279,9 @@ #. No configured account #: ../data/glade/account_modification_window.glade.h:16 #: ../data/glade/accounts_window.glade.h:20 -#: ../data/glade/roster_window.glade.h:5 ../src/common/helpers.py:1043 -#: ../src/common/helpers.py:1055 ../src/notify.py:501 ../src/notify.py:532 -#: ../src/notify.py:544 +#: ../data/glade/roster_window.glade.h:5 ../src/common/helpers.py:1034 +#: ../src/common/helpers.py:1046 ../src/notify.py:515 ../src/notify.py:546 +#: ../src/notify.py:558 msgid "Gajim" msgstr "Gajim"
@@ -309,8 +293,8 @@ #: ../data/glade/preferences_window.glade.h:49 #: ../data/glade/zeroconf_properties_window.glade.h:7 #: ../src/common/contacts.py:98 ../src/dialogs.py:103 ../src/dialogs.py:111 -#: ../src/dialogs.py:166 ../src/roster_window.py:2673 -#: ../src/roster_window.py:5158 +#: ../src/dialogs.py:166 ../src/roster_window.py:2654 +#: ../src/roster_window.py:5137 msgid "General" msgstr "General"
@@ -330,13 +314,12 @@
#: ../data/glade/account_modification_window.glade.h:21 #: ../data/glade/accounts_window.glade.h:26 -#, fuzzy msgid "" "If checked, Gajim will send keep-alive packets to prevent connection timeout " "which results in disconnection" msgstr "" "Si está marcado, Gajim enviará paquetes de mantenimiento de actividad para " -"prevenir que expire la conexión" +"prevenir desconexiones por expiración"
#: ../data/glade/account_modification_window.glade.h:22 #: ../data/glade/zeroconf_properties_window.glade.h:8 @@ -376,20 +359,24 @@
#: ../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:1557 -#: ../src/config.py:2057 +#: ../data/glade/zeroconf_properties_window.glade.h:16 ../src/config.py:1611 +#: ../src/config.py:2111 msgid "No key selected" msgstr "Ninguna clave seleccionada"
#. None means no proxy profile selected +#. boshuseproxy_checkbutton.set_active(False) +#. self.on_boshuseproxy_checkbutton_toggled(boshuseproxy_checkbutton) +#. useauth_checkbutton.set_active(False) +#. self.on_useauth_checkbutton_toggled(useauth_checkbutton) #: ../data/glade/account_modification_window.glade.h:29 #: ../data/glade/accounts_window.glade.h:38 -#: ../data/glade/change_mood_dialog.glade.h:3 ../src/config.py:1147 -#: ../src/config.py:1218 ../src/config.py:1465 ../src/config.py:1470 -#: ../src/config.py:1965 ../src/config.py:2043 ../src/config.py:2056 -#: ../src/config.py:3151 ../src/config.py:3219 ../src/dialogs.py:294 -#: ../src/dialogs.py:296 ../src/dialogs.py:523 ../src/roster_window.py:2742 -#: ../src/roster_window.py:2748 ../src/roster_window.py:2753 +#: ../data/glade/change_mood_dialog.glade.h:3 ../src/config.py:1162 +#: ../src/config.py:1251 ../src/config.py:1519 ../src/config.py:1524 +#: ../src/config.py:2019 ../src/config.py:2097 ../src/config.py:2110 +#: ../src/config.py:3201 ../src/config.py:3269 ../src/dialogs.py:294 +#: ../src/dialogs.py:296 ../src/dialogs.py:522 ../src/roster_window.py:2723 +#: ../src/roster_window.py:2729 ../src/roster_window.py:2734 msgid "None" msgstr "Ninguno"
@@ -432,7 +419,6 @@
#: ../data/glade/account_modification_window.glade.h:36 #: ../data/glade/accounts_window.glade.h:45 -#, fuzzy msgid "Resour_ce:" msgstr "Re_curso: "
@@ -478,9 +464,8 @@ #: ../data/glade/accounts_window.glade.h:51 #: ../data/glade/synchronise_contacts_dialog.glade.h:2 #: ../data/glade/synchronise_select_account_dialog.glade.h:2 -#, fuzzy msgid "Synchronise contacts" -msgstr "Mostrar contactos desconectados" +msgstr "Sincronizar contactos"
#: ../data/glade/account_modification_window.glade.h:44 msgid "Use _SSL (legacy)" @@ -502,14 +487,12 @@ msgstr "ID de _Jabber: "
#: ../data/glade/account_modification_window.glade.h:49 -#, fuzzy msgid "_Name:" msgstr "_Nombre: "
#: ../data/glade/accounts_window.glade.h:4 -#, fuzzy msgid "<b>Proxy</b>" -msgstr "<b>Registro</b>" +msgstr "<b>Proxy</b>"
#: ../data/glade/accounts_window.glade.h:6 msgid "Accounts" @@ -520,6 +503,8 @@ "Check this so Gajim will ask you before sending your password over an " "insecure connection." msgstr "" +"Marca esto para que Gajim te pregunte antes de enviar tu contraseña a través " +"de una conexión no segura."
#: ../data/glade/accounts_window.glade.h:16 #: ../data/glade/zeroconf_information_window.glade.h:2 @@ -540,9 +525,9 @@ msgstr "Nombre:"
#: ../data/glade/accounts_window.glade.h:24 -#, fuzzy msgid "If checked, Gajim will get the password from a GPG agent like seahorse" -msgstr "Si está marcado, Gajim recordará la contraseña para esta cuenta" +msgstr "" +"Si está marcado, Gajim obtendrá la contraseña de un agente GPG como seahorse"
#: ../data/glade/accounts_window.glade.h:29 #: ../data/glade/zeroconf_properties_window.glade.h:11 @@ -558,7 +543,7 @@
#: ../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:688 +#: ../data/glade/zeroconf_properties_window.glade.h:13 ../src/dialogs.py:678 msgid "Jabber ID:" msgstr "ID de Jabber:"
@@ -573,13 +558,13 @@ msgstr "_Combinar las cuentas"
#. Rename -#: ../data/glade/accounts_window.glade.h:44 ../src/roster_window.py:5109 +#: ../data/glade/accounts_window.glade.h:44 ../src/roster_window.py:5088 msgid "Re_name" msgstr "Re_nombrar"
#: ../data/glade/accounts_window.glade.h:52 msgid "Use GPG _Agent" -msgstr "" +msgstr "Usar _agente GPG"
#: ../data/glade/accounts_window.glade.h:54 #: ../data/glade/zeroconf_properties_window.glade.h:21 @@ -588,7 +573,7 @@
#: ../data/glade/accounts_window.glade.h:59 msgid "_Warn before using an insecure connection" -msgstr "" +msgstr "_Advertir antes de usar una conexión no segura"
#: ../data/glade/accounts_window.glade.h:60 msgid "gtk-add" @@ -605,7 +590,7 @@
#: ../data/glade/accounts_window.glade.h:63 msgid "use HTTP_PROXY environment variable" -msgstr "" +msgstr "usar variable de entorno HTTP_PROXY"
#: ../data/glade/add_new_contact_window.glade.h:1 msgid "A_ccount:" @@ -624,7 +609,6 @@ msgstr "Me gustaría añadirte a mi lista de contactos."
#: ../data/glade/add_new_contact_window.glade.h:5 -#, fuzzy msgid "" "You have to register with this transport\n" "to be able to add a contact from this\n" @@ -665,7 +649,6 @@ msgstr "ID de _usuario:"
#: ../data/glade/adhoc_commands_window.glade.h:1 -#, fuzzy msgid "<b>An error has occurred:</b>" msgstr "<b>Ha ocurrido un error:</b>"
@@ -707,7 +690,6 @@ msgstr "<b>Descripción</b>"
#: ../data/glade/advanced_configuration_window.glade.h:2 -#, fuzzy msgid "<b>NOTE:</b> You should restart Gajim for some settings to take effect" msgstr "" "<b>NOTA:</b> Debes reiniciar Gajim para que algunos cambios tengan efecto" @@ -729,19 +711,16 @@ msgstr "Elimiina mensaje del día"
#: ../data/glade/advanced_menuitem_menu.glade.h:3 -#, fuzzy msgid "Edit _Privacy Lists..." msgstr "Listas de _privacidad"
#: ../data/glade/advanced_menuitem_menu.glade.h:4 -#, fuzzy msgid "Sends a message to users currently connected to this server" msgstr "Envía un mensaje a los usuarios conectados en este momento al servidor"
#: ../data/glade/advanced_menuitem_menu.glade.h:5 -#, fuzzy msgid "Set MOTD..." -msgstr "Definir MOTD" +msgstr "Definir MOTD..."
#: ../data/glade/advanced_menuitem_menu.glade.h:6 msgid "Sets Message of the Day" @@ -752,9 +731,8 @@ msgstr "Mostrar consola _XML"
#: ../data/glade/advanced_menuitem_menu.glade.h:8 -#, fuzzy msgid "Update MOTD..." -msgstr "Actualizar MOTD" +msgstr "Actualizar MOTD..."
#: ../data/glade/advanced_menuitem_menu.glade.h:9 msgid "Updates Message of the Day" @@ -765,7 +743,6 @@ msgstr "_Administrador"
#: ../data/glade/advanced_menuitem_menu.glade.h:11 -#, fuzzy msgid "_Send Server Message..." msgstr "_Enviar mensaje de servidor"
@@ -799,7 +776,7 @@ msgstr "Todos los estados"
#: ../data/glade/advanced_notifications_window.glade.h:8 -#: ../src/common/commands.py:102 ../src/common/helpers.py:291 +#: ../src/common/commands.py:102 ../src/common/helpers.py:290 msgid "Away" msgstr "Ausente"
@@ -816,7 +793,7 @@ msgstr "Tiene "
#: ../data/glade/advanced_notifications_window.glade.h:12 -#: ../src/common/helpers.py:301 +#: ../src/common/helpers.py:300 msgid "Invisible" msgstr "Invisible"
@@ -825,7 +802,7 @@ msgstr "Lanzar un comando"
#: ../data/glade/advanced_notifications_window.glade.h:14 -#: ../src/common/helpers.py:274 +#: ../src/common/helpers.py:273 msgid "Not Available" msgstr "No disponible"
@@ -842,7 +819,6 @@ msgstr "Reproducir un _sonido"
#: ../data/glade/advanced_notifications_window.glade.h:18 -#, fuzzy msgid "" "Receive a Message\n" "Contact Disconnected \n" @@ -953,32 +929,28 @@ msgstr "Has recibido una nueva entrada:"
#: ../data/glade/blocked_contacts_window.glade.h:1 -#, fuzzy msgid "Blocked Contacts" -msgstr "Contactos" +msgstr "Contactos bloqueados"
#: ../data/glade/change_activity_dialog.glade.h:1 -#, fuzzy msgid "<b>Message:</b> " -msgstr "<b>Mensajes de estado predefinidos</b>" +msgstr "<b>Mensaje:</b>"
#: ../data/glade/change_activity_dialog.glade.h:2 msgid "Set Activity" -msgstr "" +msgstr "Definir Actividad"
#: ../data/glade/change_activity_dialog.glade.h:3 msgid "Set an activity" -msgstr "" +msgstr "Define una actividad"
#: ../data/glade/change_mood_dialog.glade.h:1 -#, fuzzy msgid "<b>Message:</b>" -msgstr "<b>Registro</b>" +msgstr "<b>Mensaje:</b>"
#: ../data/glade/change_mood_dialog.glade.h:2 -#, fuzzy msgid "<b>Mood:</b>" -msgstr "<b>Registro</b>" +msgstr "<b>Estado de ánimo</b>"
#: ../data/glade/change_password_dialog.glade.h:1 msgid "Change Password" @@ -1010,9 +982,8 @@
#: ../data/glade/chat_context_menu.glade.h:2 #: ../data/glade/roster_contact_context_menu.glade.h:13 -#, fuzzy msgid "_Add to Roster..." -msgstr "_Añadir a la lista de contactos" +msgstr "_Añadir al roster"
#: ../data/glade/chat_context_menu.glade.h:3 msgid "_Copy JID/Email Address" @@ -1037,9 +1008,8 @@ msgstr "Iniciar conversación"
#: ../data/glade/chat_control_popup_menu.glade.h:1 -#, fuzzy msgid "Invite _Contacts" -msgstr "Contactos" +msgstr "Invitar _Contactos"
#: ../data/glade/chat_control_popup_menu.glade.h:2 #: ../data/glade/gc_occupants_menu.glade.h:3 @@ -1048,13 +1018,12 @@ msgstr "Enviar archivo"
#: ../data/glade/chat_control_popup_menu.glade.h:3 -#, fuzzy msgid "Toggle End to End Encryption" -msgstr "Alternar encriptación OpenPGP" +msgstr "Alternar cifrado End to End"
#: ../data/glade/chat_control_popup_menu.glade.h:4 msgid "Toggle Open_PGP Encryption" -msgstr "Alternar encriptación OpenPGP" +msgstr "Alternar cifrado OpenPGP"
#: ../data/glade/chat_control_popup_menu.glade.h:5 #: ../data/glade/gc_occupants_menu.glade.h:4 @@ -1067,33 +1036,33 @@ #: ../data/glade/roster_contact_context_menu.glade.h:17 #: ../data/glade/roster_window.glade.h:20 #: ../data/glade/zeroconf_contact_context_menu.glade.h:6 -#: ../src/roster_window.py:5805 +#: ../src/roster_window.py:5784 msgid "_History" msgstr "_Histórico"
#: ../data/glade/chat_to_muc_window.glade.h:1 -#, fuzzy msgid "In_vite" -msgstr "In_vitar a" +msgstr "In_vitar"
#: ../data/glade/chat_to_muc_window.glade.h:2 msgid "Invite Friends!" -msgstr "" +msgstr "¡Invitar amigos!"
#: ../data/glade/chat_to_muc_window.glade.h:3 -#, fuzzy msgid "MUC server" -msgstr "Observadores" +msgstr "Servidor MUC"
#: ../data/glade/chat_to_muc_window.glade.h:4 msgid "Please select a MUC server." -msgstr "" +msgstr "Por favor selecciona un servidor MUC."
#: ../data/glade/chat_to_muc_window.glade.h:5 msgid "" "You are going to begin a Multi-User Chat.\n" "Select the contacts you want to invite" msgstr "" +"Vas a comenzar una conversación multiusuario.\n" +"Selecciona los contactos a invitar"
#: ../data/glade/data_form_window.glade.h:1 msgid "Fill in the form." @@ -1108,22 +1077,20 @@ msgstr "Editar grupos"
#: ../data/glade/esession_info_window.glade.h:1 -#, fuzzy msgid "(<b>ESession</b> info)" -msgstr "<b>Descripción</b>" +msgstr "Información <b>ESession</b>"
#: ../data/glade/esession_info_window.glade.h:2 msgid "_Verify" -msgstr "" +msgstr "_Verificar"
#: ../data/glade/features_window.glade.h:2 msgid "<b>List of possible features in Gajim:</b>" -msgstr "" +msgstr "<b>Lista de posibles características en Gajim</b>"
#: ../data/glade/features_window.glade.h:3 -#, fuzzy msgid "Features" -msgstr "Características de los servidores" +msgstr "Características"
#: ../data/glade/filetransfers.glade.h:1 msgid "A list of active, completed and stopped file transfers" @@ -1160,15 +1127,13 @@ msgstr "Eliminar transferencias de la lista"
#: ../data/glade/filetransfers.glade.h:9 -#, fuzzy msgid "Removes completed, cancelled and failed file transfers from the list" msgstr "" "Elimina de la lista las transferencias finalizadas, canceladas y fallidas"
#: ../data/glade/filetransfers.glade.h:10 -#, fuzzy msgid "Shows a list of file transfers between you and others" -msgstr "Muestra una lista de transferencias entre tú y otro" +msgstr "Muestra una lista de transferencias entre tú y otros"
#: ../data/glade/filetransfers.glade.h:11 msgid "" @@ -1183,7 +1148,7 @@ msgstr "" "Cuando una transferencia se completa, muestra una notificación emergente"
-#: ../data/glade/filetransfers.glade.h:13 ../src/filetransfers_window.py:793 +#: ../data/glade/filetransfers.glade.h:13 ../src/filetransfers_window.py:792 msgid "_Continue" msgstr "_Continuar"
@@ -1219,7 +1184,7 @@ "Contacto\n" "Banner"
-#: ../data/glade/gajim_themes_window.glade.h:6 +#: ../data/glade/gajim_themes_window.glade.h:6 ../src/chat_control.py:743 msgid "Bold" msgstr "Negrita"
@@ -1243,7 +1208,7 @@ msgid "Inactive" msgstr "Inactivo"
-#: ../data/glade/gajim_themes_window.glade.h:12 +#: ../data/glade/gajim_themes_window.glade.h:12 ../src/chat_control.py:744 msgid "Italic" msgstr "Cursiva"
@@ -1280,46 +1245,40 @@ msgstr "_Fondo"
#: ../data/glade/gc_control_popup_menu.glade.h:1 -#, fuzzy msgid "Change _Nickname..." msgstr "Cambiar _Alias"
#: ../data/glade/gc_control_popup_menu.glade.h:2 -#, fuzzy msgid "Change _Subject..." msgstr "Cambiar el _tema"
#: ../data/glade/gc_control_popup_menu.glade.h:3 -#, fuzzy msgid "Configure _Room..." msgstr "Configurar el salón"
#: ../data/glade/gc_control_popup_menu.glade.h:4 -#, fuzzy msgid "_Bookmark" -msgstr "Añadir este salón a marcadores" +msgstr "_Marcador"
#: ../data/glade/gc_control_popup_menu.glade.h:5 msgid "_Destroy Room" -msgstr "" +msgstr "_Desmantelar salón"
#: ../data/glade/gc_control_popup_menu.glade.h:7 -#, fuzzy msgid "_Manage Room" -msgstr "Gestionar marcadores" +msgstr "_Gestionar salón"
#: ../data/glade/gc_control_popup_menu.glade.h:8 msgid "_Minimize on close" -msgstr "" +msgstr "_Minimizar al cerrar"
#: ../data/glade/gc_occupants_menu.glade.h:1 msgid "Mo_derator" msgstr "Mo_derador"
#: ../data/glade/gc_occupants_menu.glade.h:2 -#, fuzzy msgid "Occupant Actions" -msgstr "_Acciones" +msgstr "Acciones de ocupante"
#: ../data/glade/gc_occupants_menu.glade.h:5 msgid "_Admin" @@ -1353,7 +1312,7 @@ msgid "Create new post" msgstr "Crear nuevo post"
-#: ../data/glade/groups_post_window.glade.h:2 ../src/common/helpers.py:316 +#: ../data/glade/groups_post_window.glade.h:2 ../src/common/helpers.py:315 msgid "From" msgstr "De"
@@ -1364,26 +1323,19 @@ msgstr "Tema"
#: ../data/glade/history_manager.glade.h:1 -#, fuzzy msgid "" "<b>WARNING:</b>\n" "If you plan to do massive deletions, please make sure Gajim is not running. " "Generally avoid deletions with contacts you currently chat with." msgstr "" -"<big><b>Bienvenido al Gestor del Histórico de Gajim</b></big>\n" -"\n" -"You can select logs from the left and/or search database from below.\n" -"\n" "<b>ADVERTENCIA:</b>\n" "Si tienes previsto hacer eliminaciones masivas, asegúrate de que Gajim\n" -"no se encuentre en ejecución.Generalmente evita eliminaciones relativas a " -"contactos con los que te\n" -"encuentres hablando en este momento." +"no se encuentre en ejecución. Generalmente evita eliminaciones relativas a " +"contactos con los que te encuentres hablando en este momento."
#: ../data/glade/history_manager.glade.h:3 -#, fuzzy msgid "<big><b>Welcome to Gajim History Logs Manager</b></big>" -msgstr "Gestor de Histórico de Gajim" +msgstr "<big><b>Bienvenido al gestor de históricos de Gajim</b></big>"
#: ../data/glade/history_manager.glade.h:4 msgid "Delete" @@ -1405,12 +1357,17 @@ "Use this program to delete or export logs. You can select logs from the left " "and/or search database from below." msgstr "" +"Este gestor de registros no está pensado para ver registros. Si quieres " +"dicha funcionalidad usa la ventana del histórico en su lugar.\n" +"\n" +"Usa este programa para eliminar o exportar registros. Puedes seleccionar " +"registros de la parte izquierda y/o buscar en la base de datos abajo."
#: ../data/glade/history_manager.glade.h:10 msgid "_Search Database" msgstr "_Buscar en la base de datos"
-#: ../data/glade/history_window.glade.h:1 ../src/history_window.py:305 +#: ../data/glade/history_window.glade.h:1 ../src/history_window.py:316 msgid "Conversation History" msgstr "Histórico de conversaciones"
@@ -1420,9 +1377,11 @@ "Groupchat Histories\n" "All Chat Histories" msgstr "" +"Introduce JID o nombre de contacto\n" +"Históricos de charlas de grupo\n" +"Todos los históricos de charlas"
#: ../data/glade/history_window.glade.h:5 -#, fuzzy msgid "Search:" msgstr "Buscar"
@@ -1431,14 +1390,13 @@ msgid "_Log conversation history" msgstr "Histórico de conversaciones"
-#: ../data/glade/join_groupchat_window.glade.h:1 ../src/dialogs.py:1707 +#: ../data/glade/join_groupchat_window.glade.h:1 ../src/dialogs.py:1697 msgid "Join Group Chat" msgstr "Entrar a un salón de chat"
#: ../data/glade/join_groupchat_window.glade.h:2 -#, fuzzy msgid "Join this room automatically when I connect" -msgstr "Definir mi perfil cuando me conecte" +msgstr "Entrar en este salón automáticamente al conectarme"
#: ../data/glade/join_groupchat_window.glade.h:3 #: ../data/glade/manage_bookmarks_window.glade.h:5 @@ -1459,7 +1417,7 @@ msgid "Room:" msgstr "Salón:"
-#: ../data/glade/join_groupchat_window.glade.h:7 ../src/disco.py:1197 +#: ../data/glade/join_groupchat_window.glade.h:7 ../src/disco.py:1198 #: ../src/disco.py:1618 msgid "_Join" msgstr "_Entrar" @@ -1482,7 +1440,7 @@
#: ../data/glade/manage_bookmarks_window.glade.h:4 msgid "Minimize on Auto Join" -msgstr "" +msgstr "Minimizar al ingresar automáticamente"
#: ../data/glade/manage_bookmarks_window.glade.h:7 msgid "Print status:" @@ -1497,19 +1455,16 @@ msgstr "Título:"
#: ../data/glade/manage_pep_services_window.glade.h:1 -#, fuzzy msgid "PEP Service Configuration" -msgstr "Configuración del salón" +msgstr "Configuración del servicio PEP"
#: ../data/glade/manage_pep_services_window.glade.h:2 -#, fuzzy msgid "_Configure" -msgstr "_Continuar" +msgstr "_Configurar"
#: ../data/glade/manage_pep_services_window.glade.h:3 -#, fuzzy msgid "gtk-delete" -msgstr "Eliminar" +msgstr "gtk-delete"
#: ../data/glade/manage_proxies_window.glade.h:1 msgid "<b>Properties</b>" @@ -1521,82 +1476,106 @@
#: ../data/glade/manage_proxies_window.glade.h:3 #, fuzzy +msgid "B_OSH Port:" +msgstr "_Puerto:" + +#: ../data/glade/manage_proxies_window.glade.h:4 +#, fuzzy msgid "" "HTTP Connect\n" +"SOCKS5\n" +"BOSH" +msgstr "" +"Conexión HTTP\n" "SOCKS5" -msgstr "Conectar con HTTP"
-#: ../data/glade/manage_proxies_window.glade.h:5 +#: ../data/glade/manage_proxies_window.glade.h:7 msgid "Manage Proxy Profiles" msgstr "Gestionar perfiles del Proxy"
-#: ../data/glade/manage_proxies_window.glade.h:6 +#: ../data/glade/manage_proxies_window.glade.h:8 msgid "Name:" msgstr "Nombre:"
-#: ../data/glade/manage_proxies_window.glade.h:7 +#: ../data/glade/manage_proxies_window.glade.h:9 msgid "Pass_word:" msgstr "_Contraseña: "
-#: ../data/glade/manage_proxies_window.glade.h:8 +#: ../data/glade/manage_proxies_window.glade.h:10 +#, fuzzy +msgid "Proxy _Host:" +msgstr "Proxy" + +#: ../data/glade/manage_proxies_window.glade.h:11 +#, fuzzy +msgid "Proxy _Port:" +msgstr "_Puerto:" + +#: ../data/glade/manage_proxies_window.glade.h:12 msgid "Type:" msgstr "Tipo:"
-#: ../data/glade/manage_proxies_window.glade.h:9 -msgid "Use authentication" +#: ../data/glade/manage_proxies_window.glade.h:13 +#, fuzzy +msgid "Use HTTP proxy" +msgstr "_Usar proxy" + +#: ../data/glade/manage_proxies_window.glade.h:14 +#, fuzzy +msgid "Use proxy authentication" msgstr "Usar autentificación"
-#: ../data/glade/manage_proxies_window.glade.h:10 -msgid "_Host:" -msgstr "_Servidor" +#: ../data/glade/manage_proxies_window.glade.h:15 +msgid "_BOSH URL:" +msgstr ""
#: ../data/glade/message_window.glade.h:1 -#, fuzzy msgid "Add this contact to roster (Ctrl+D)" -msgstr "Añade contacto al roster" +msgstr "Añade éste contacto al roster (Ctrl+D)"
#: ../data/glade/message_window.glade.h:2 -#, fuzzy msgid "Bookmark this room (Ctrl+B)" -msgstr "Añadir este salón a marcadores" +msgstr "Añadir este salón a marcadores (Ctrl+B)"
#: ../data/glade/message_window.glade.h:3 msgid "Browse the chat history (Ctrl+H)" -msgstr "" +msgstr "Navegar en el histórico de la conversación (Ctrl+H)"
#: ../data/glade/message_window.glade.h:4 msgid "Change the room's subject (Alt+T)" -msgstr "" +msgstr "Cambiar el tema del salón (Alt+T)"
#: ../data/glade/message_window.glade.h:5 -#, fuzzy msgid "Change your nickname (Ctrl+N)" -msgstr "Cambiar _Alias" +msgstr "Cambiar tu _alias (Ctrl+N)"
#: ../data/glade/message_window.glade.h:6 -#, fuzzy msgid "Invite contacts to the conversation (Ctrl+G)" -msgstr "está prestando atención a la conversación" +msgstr "Invitar contactos a esta conversación (Ctrl+G)"
#: ../data/glade/message_window.glade.h:7 msgid "Send a file (Ctrl+F)" -msgstr "" +msgstr "Enviar un archivo (Ctrl+F)"
#: ../data/glade/message_window.glade.h:8 -#, fuzzy msgid "Show a list of emoticons (Alt+M)" -msgstr "Haz click para insertar un emoticono (Alt+M)" +msgstr "Mostrar una lista de emoticonos (Alt+M)"
#: ../data/glade/message_window.glade.h:9 -msgid "Show a menu of advanced functions (Alt+A)" -msgstr "" +#, fuzzy +msgid "Show a list of formattings" +msgstr "Mostrar una lista de emoticonos (Alt+M)"
#: ../data/glade/message_window.glade.h:10 +msgid "Show a menu of advanced functions (Alt+A)" +msgstr "Muestra un menú de funciones avanzadas (Alt+A)" + +#: ../data/glade/message_window.glade.h:11 msgid "Show the contact's profile (Ctrl+I)" -msgstr "" +msgstr "Muestra el perfil del contacto (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 +#: ../data/glade/message_window.glade.h:13 #: ../data/glade/xml_console_window.glade.h:11 #: ../src/filetransfers_window.py:261 msgid "_Send" @@ -1615,24 +1594,20 @@ msgstr "<b>Programas</b>"
#: ../data/glade/preferences_window.glade.h:3 -#, fuzzy msgid "<b>Auto Status</b>" -msgstr "<b>Acciones</b>" +msgstr "<b>Estado automático</b>"
#: ../data/glade/preferences_window.glade.h:4 -#, fuzzy msgid "<b>Chat Appearance</b>" -msgstr "<b>Editar una regla</b>" +msgstr "<b>Apariencia de la conversación</b>"
#: ../data/glade/preferences_window.glade.h:5 -#, fuzzy msgid "<b>Chat Line Colors</b>" -msgstr "<b>Colores de la pestaña para los estados de conversación</b>" +msgstr "<b>Color de las líneas de la conversación</b>"
#: ../data/glade/preferences_window.glade.h:6 -#, fuzzy msgid "<b>Chat state notifications</b>" -msgstr "<b>Notificationes Visuales</b>" +msgstr "<b>Notificación del estado de la conversación</b>"
#. a header for custom browser/client/file manager. so translate sth like: Custom Settings #: ../data/glade/preferences_window.glade.h:8 @@ -1640,9 +1615,8 @@ msgstr "<b>Personalizado</b>"
#: ../data/glade/preferences_window.glade.h:9 -#, fuzzy msgid "<b>Font</b>" -msgstr "<b>Registro</b>" +msgstr "<b>Fuente</b>"
#: ../data/glade/preferences_window.glade.h:10 msgid "<b>GMail Options</b>" @@ -1653,23 +1627,20 @@ msgstr "<b>Mensajes de estado predefinidos</b>"
#: ../data/glade/preferences_window.glade.h:13 -#, fuzzy msgid "<b>Privacy</b>" -msgstr "<i>Lista de privacidad</i>" +msgstr "<i>Privacidad</i>"
#: ../data/glade/preferences_window.glade.h:14 msgid "<b>Roster Appearance</b>" -msgstr "" +msgstr "<b>Roster Appearance</b>"
#: ../data/glade/preferences_window.glade.h:16 -#, fuzzy msgid "<b>Status Messages</b>" -msgstr "<b>Mensajes de estado predefinidos</b>" +msgstr "<b>Mensajes de estado</b>"
#: ../data/glade/preferences_window.glade.h:17 -#, fuzzy msgid "<b>Themes</b>" -msgstr "<b>Propiedades</b>" +msgstr "<b>Temas</b>"
#: ../data/glade/preferences_window.glade.h:18 msgid "<b>Visual Notifications</b>" @@ -1680,9 +1651,8 @@ msgstr "Avanzado"
#: ../data/glade/preferences_window.glade.h:20 -#, fuzzy msgid "Advanced..." -msgstr "Avanzado" +msgstr "Avanzado..."
#: ../data/glade/preferences_window.glade.h:21 msgid "" @@ -1709,7 +1679,6 @@ msgstr "Preguntar mensaje de estado cuando esté: "
#: ../data/glade/preferences_window.glade.h:27 -#, fuzzy msgid "" "Autodetect on every Gajim startup\n" "Always use GNOME default applications\n" @@ -1720,17 +1689,16 @@ "Autodetectar en cada inicio de Gajim\n" "Siempre usar las aplicaciones por defecto de GNOME\n" "Siempre usar las apliacciones por defecto de KDE\n" -"Siempre usar las aplicaciones por defecto de XFCE4\n" +"Siempre usar las aplicaciones por defecto de XFCE\n" "Personalizado"
#: ../data/glade/preferences_window.glade.h:32 -#, fuzzy msgid "Chat message:" -msgstr "Men_saje de estado:" +msgstr "Mensaje de conversación:"
#: ../data/glade/preferences_window.glade.h:33 msgid "Check on startup if Gajim is the _default Jabber client" -msgstr "" +msgstr "Comprobar al inicio si Gajim es el cliente de Jabber por defecto"
#: ../data/glade/preferences_window.glade.h:34 msgid "Configure color and font of the interface" @@ -1744,29 +1712,31 @@ "Detached roster with chat grouped by account\n" "Detached roster with chat grouped by type" msgstr "" +"Roster desacoplado con conversaciones desacopladas\n" +"Roster desacoplado con conversaciones independientes\n" +"Ventana independiente para todo\n" +"Roster desacoplado con conversaciones agrupadas por cuenta\n" +"Roster desacoplado con conversaciones agrupadas por tipo"
#: ../data/glade/preferences_window.glade.h:40 -#, fuzzy msgid "Display _activity of contacts in roster" -msgstr "Mostrar en el roster los a_vatares de los contactos" +msgstr "Mostrar la _actividad de los contactos en el roster"
#: ../data/glade/preferences_window.glade.h:41 msgid "Display _extra email details" msgstr "Mostrar detalles _extra en los correos electrónicos"
#: ../data/glade/preferences_window.glade.h:42 -#, fuzzy msgid "Display _tunes of contacts in roster" -msgstr "Mostrar en el roster los a_vatares de los contactos" +msgstr "Mostrar _ajustes de los contactos en el roster"
#: ../data/glade/preferences_window.glade.h:43 msgid "Display a_vatars of contacts in roster" msgstr "Mostrar en el roster los a_vatares de los contactos"
#: ../data/glade/preferences_window.glade.h:44 -#, fuzzy msgid "Display m_ood of contacts in roster" -msgstr "Mostrar en el roster los a_vatares de los contactos" +msgstr "Mostrar estad_o de ánimo de los contactos en el roster"
#: ../data/glade/preferences_window.glade.h:45 msgid "Display status _messages of contacts in roster" @@ -1801,16 +1771,16 @@ "pantalla cuando un contacto se desconecte"
#: ../data/glade/preferences_window.glade.h:50 -#, fuzzy msgid "Hide all buttons in chat windows" -msgstr "Oculta los botones en las ventanas de grupo de charla." +msgstr "Oculta los botones en las ventanas de conversación."
#: ../data/glade/preferences_window.glade.h:51 -#, fuzzy msgid "" "If checked, Gajim will allow others to detect the operation system you are " "using" -msgstr "Si está marcado, Gajim entrará a este salón al inicio" +msgstr "" +"Si está marcado, Gajim permitirá a otros detectar el sistema operativo que " +"estás usando"
#: ../data/glade/preferences_window.glade.h:52 msgid "" @@ -1818,21 +1788,22 @@ "emails" msgstr "" "Si está marcado, Gajim también incluirá información acerca del remitente de " -"nuevos correos electrónicos" +"los nuevos correos electrónicos"
#: ../data/glade/preferences_window.glade.h:53 -#, fuzzy msgid "" "If checked, Gajim will change status to Away when the computer is unused." msgstr "" -"Si está marcado, Gajim alojará un icono en el área de notificación del " -"sistema" +"Si está marcado, Gajim cambiará el estado a Ausente cuando el ordenador no " +"esté en uso"
#: ../data/glade/preferences_window.glade.h:54 msgid "" "If checked, Gajim will change status to Not Available when the computer has " "not been used even longer" msgstr "" +"Si está marcado, Gajim cambiará el estado a no disponible cuando el " +"ordenador ya no se use"
#: ../data/glade/preferences_window.glade.h:55 msgid "" @@ -1851,28 +1822,23 @@ "el nombre de contacto en el roster y grupos de conversación"
#: ../data/glade/preferences_window.glade.h:57 -#, fuzzy msgid "" "If checked, Gajim will display the activity of contacts in the roster window" msgstr "" -"Si está marcado, Gajim mostrará los avatares de los contactos en el roster y " -"grupos de conversación" +"Si está marcado, Gajim mostrará la actividad de los contactos en el roster"
#: ../data/glade/preferences_window.glade.h:58 -#, fuzzy msgid "" "If checked, Gajim will display the mood of contacts in the roster window" msgstr "" -"Si está marcado, Gajim mostrará los avatares de los contactos en el roster y " -"grupos de conversación" +"Si está marcado, Gajim mostrará el estado de ánimo de los contactos en el " +"roster"
#: ../data/glade/preferences_window.glade.h:59 -#, fuzzy msgid "" "If checked, Gajim will display the tunes of contacts in the roster window" msgstr "" -"Si está marcado, Gajim mostrará los avatares de los contactos en el roster y " -"grupos de conversación" +"Si está marcado, Gajim mostrará los ajustes de los contactos en el roster"
#: ../data/glade/preferences_window.glade.h:60 msgid "" @@ -1880,17 +1846,19 @@ "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 "" +"Si está marcado, Gajim resaltará errores de escritura en la entrada de las " +"ventanas de conversación. Si no se definió ningún idioma vía botón derecho " +"en el campo de entrada, será usado el idioma por defecto."
#: ../data/glade/preferences_window.glade.h:61 -#, fuzzy msgid "" "If checked, Gajim will ignore incoming events from unauthorized contacts. " "Use with caution, because it blocks all messages from any contact that is " "not in the roster" msgstr "" -"Marca esta opción sólo si alguien que no tienes en el roster te molesta. " -"Úsalo con precaución ya que bloqueará todos los mensajes de cualquier " -"contacto que no esté en tu lista de contactos." +"Si está marcado, Gajim ignorará eventos entrantes de contactos no " +"autorizados. Úsalo con precaución ya que bloqueará todos los mensajes de " +"cualquier contacto que no esté en tu lista de contactos."
#: ../data/glade/preferences_window.glade.h:62 msgid "" @@ -1898,15 +1866,17 @@ "when using E2E encryption the remote party has to agree on logging, else the " "messages will not be logged." msgstr "" +"Si está marcado, Gajim guardará los registros de mensajes cifrados. Ten en " +"cuenta que usando cifrado E2E, la parte remota tiene que aceptar al " +"ingresar, o los mensajes no serán registrados."
#: ../data/glade/preferences_window.glade.h:63 -#, fuzzy msgid "" "If checked, Gajim will show a notification when a new e-mail is received via " "GMail" msgstr "" -"Si está marcado, Gajim también incluirá información acerca del remitente de " -"nuevos correos electrónicos" +"Si está marcado, Gajim mostrará una notificación cuando se reciba un nuevo " +"correo vía GMail"
#: ../data/glade/preferences_window.glade.h:64 msgid "" @@ -1923,6 +1893,8 @@ "If enabled, Gajim will not ask for a status message. The specified default " "message will be used instead." msgstr "" +"Si está habilitado, Gajim no preguntará un mensaje de estado. Será usado el " +"mensaje por defecto especificado."
#: ../data/glade/preferences_window.glade.h:66 msgid "" @@ -1934,29 +1906,25 @@
#: ../data/glade/preferences_window.glade.h:67 msgid "Log _encrypted chat session" -msgstr "" +msgstr "R_egistrar conversación cifrada"
#: ../data/glade/preferences_window.glade.h:68 -#, fuzzy msgid "Ma_ke message windows compact" -msgstr "Una sóla ventana de conversación" +msgstr "Hacer compacta la ventana de mensajes"
#: ../data/glade/preferences_window.glade.h:69 msgid "Ma_nage..." msgstr "Gestio_nar..."
#: ../data/glade/preferences_window.glade.h:70 -#, fuzzy msgid "Notifications" -msgstr "Modificar cuenta" +msgstr "Notificaciones"
#: ../data/glade/preferences_window.glade.h:71 -#, fuzzy msgid "Notify me about contacts that sign _in" -msgstr "Notificar cuand un contacto: " +msgstr "Notif_icarme de los contactos que se conecten"
#: ../data/glade/preferences_window.glade.h:72 -#, fuzzy msgid "Notify me about contacts that sign _out" msgstr "Notificar cuand un contacto: "
@@ -1965,9 +1933,8 @@ msgstr "Notificar de nuevos correos de _GMail"
#: ../data/glade/preferences_window.glade.h:74 -#, fuzzy msgid "Personal Events" -msgstr "Información personal" +msgstr "Eventos personales"
#: ../data/glade/preferences_window.glade.h:75 msgid "Play _sounds" @@ -1979,6 +1946,9 @@ "Notify me about it\n" "Show only in roster" msgstr "" +"Emerger aviso\n" +"Notificarmelo\n" +"Mostrar sólo en roster"
#: ../data/glade/preferences_window.glade.h:79 msgid "Preferences" @@ -2001,23 +1971,20 @@ "etc). Si está marcado, Gajim mostrará el mensaje en bruto."
#: ../data/glade/preferences_window.glade.h:83 -#, fuzzy msgid "Sort contacts by status" -msgstr "Ordenar contacto_s por estado" +msgstr "Ordenar contactos por estado"
-#: ../data/glade/preferences_window.glade.h:84 ../src/config.py:400 +#: ../data/glade/preferences_window.glade.h:84 ../src/config.py:397 msgid "Status" msgstr "Estado"
#: ../data/glade/preferences_window.glade.h:85 -#, fuzzy msgid "Status _iconset:" -msgstr "Set de _iconos de estado por defecto:" +msgstr "Set de _iconos de estado:"
#: ../data/glade/preferences_window.glade.h:86 -#, fuzzy msgid "Style" -msgstr "Atascado" +msgstr "Estilo"
#: ../data/glade/preferences_window.glade.h:87 msgid "T_heme:" @@ -2028,45 +1995,44 @@ "The auto away status message. If empty, Gajim will not change the current " "status message" msgstr "" +"El mensje de autoestado ausente. Si está vacío, Gajim no cambiará el mensaje " +"de estado actual"
#: ../data/glade/preferences_window.glade.h:89 msgid "" "The auto not available status message. If empty, Gajim will not change the " "current status message" msgstr "" +"El mensaje de autoestado no disponible. Si está vacío, Gajim no cambiará el " +"mensaje de estado actual"
#: ../data/glade/preferences_window.glade.h:90 -#, fuzzy msgid "Use _transports icons" -msgstr "_Usar los iconos del transporte" +msgstr "Usar iconos de _transporte"
#: ../data/glade/preferences_window.glade.h:91 msgid "Use system _default" msgstr "Usar pre_definido del sistema"
#: ../data/glade/preferences_window.glade.h:92 -#, fuzzy msgid "When new event is received:" -msgstr "Cuando se reciba un nuevo evento" +msgstr "Cuando se recibe un nuevo evento:"
#: ../data/glade/preferences_window.glade.h:93 -#, fuzzy msgid "_Away after:" -msgstr "Poner _ausente después de:" +msgstr "_Ausente tras:"
#: ../data/glade/preferences_window.glade.h:94 msgid "_Browser:" msgstr "_Navegador:"
#: ../data/glade/preferences_window.glade.h:95 -#, fuzzy msgid "_Display chat state notifications:" -msgstr "Notificaciones de estado en las conversaciones:" +msgstr "_Mostrar notificaciones de estado de la conversación:"
#: ../data/glade/preferences_window.glade.h:96 -#, fuzzy msgid "_Emoticons:" -msgstr "Emoticonos:" +msgstr "_Emoticonos:"
#: ../data/glade/preferences_window.glade.h:97 msgid "_File manager:" @@ -2081,9 +2047,8 @@ msgstr "_Ignorar mensajes de contactos que no están en la lista de contactos"
#: ../data/glade/preferences_window.glade.h:100 -#, fuzzy msgid "_Ignore rich content in incoming messages" -msgstr "Ignorar contenido enriquecido en mensajes entrantes" +msgstr "_Ignorar contenido enriquecido en mensajes entrantes"
#: ../data/glade/preferences_window.glade.h:101 msgid "_Incoming message:" @@ -2098,9 +2063,8 @@ msgstr "_Cliente de correo:"
#: ../data/glade/preferences_window.glade.h:104 -#, fuzzy msgid "_Not available after:" -msgstr "Poner _no disponible después de:" +msgstr "_No disponible tras:"
#: ../data/glade/preferences_window.glade.h:105 msgid "_Open..." @@ -2115,9 +2079,8 @@ msgstr "_Restablecer colores por defecto"
#: ../data/glade/preferences_window.glade.h:108 -#, fuzzy msgid "_Send chat state notifications:" -msgstr "Notificaciones de estado en las conversaciones:" +msgstr "Enviar notificaciones del estado de la conversación:"
#: ../data/glade/preferences_window.glade.h:109 msgid "_Status message:" @@ -2125,21 +2088,19 @@
#: ../data/glade/preferences_window.glade.h:110 msgid "_URL highlight:" -msgstr "" +msgstr "Resaltado de _URL:"
#: ../data/glade/preferences_window.glade.h:111 msgid "_Window behavior:" -msgstr "" +msgstr "_Comportamiento de la ventana:"
#: ../data/glade/preferences_window.glade.h:112 -#, fuzzy msgid "in _group chats" -msgstr "Entrar a un grupo de charla" +msgstr "en _grupos de charla"
#: ../data/glade/preferences_window.glade.h:113 -#, fuzzy msgid "in _roster" -msgstr "no está en el roster" +msgstr "en _roster"
#: ../data/glade/preferences_window.glade.h:114 msgid "minutes" @@ -2189,7 +2150,7 @@ msgid "Order:" msgstr "Orden:"
-#: ../data/glade/privacy_list_window.glade.h:11 ../src/dialogs.py:2574 +#: ../data/glade/privacy_list_window.glade.h:11 ../src/dialogs.py:2572 msgid "Privacy List" msgstr "Lista de privacidad"
@@ -2219,7 +2180,7 @@
#: ../data/glade/privacy_list_window.glade.h:19 msgid "to send me queries" -msgstr "" +msgstr "para enviarme consultas"
#: ../data/glade/privacy_list_window.glade.h:20 msgid "to send me status" @@ -2231,151 +2192,127 @@
#: ../data/glade/profile_window.glade.h:1 #: ../data/glade/vcard_information_window.glade.h:1 -#, fuzzy msgid "<b>Address</b>" -msgstr "<b>Añadir una regla</b>" +msgstr "<b>Dirección</b>"
#: ../data/glade/profile_window.glade.h:2 -#, fuzzy msgid "<b>Avatar:</b>" -msgstr "Avatar:" +msgstr "<b>Avatar:</b>"
#: ../data/glade/profile_window.glade.h:3 #: ../data/glade/vcard_information_window.glade.h:4 -#, fuzzy msgid "<b>Birthday:</b>" -msgstr "Cumpleaños:" +msgstr "<b>Cumpleaños:</b>"
#: ../data/glade/profile_window.glade.h:4 #: ../data/glade/vcard_information_window.glade.h:5 -#, fuzzy msgid "<b>City:</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Ciudad:Personalizado</b>"
#: ../data/glade/profile_window.glade.h:5 #: ../data/glade/vcard_information_window.glade.h:7 -#, fuzzy msgid "<b>Company:</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Compañía:</b>"
#: ../data/glade/profile_window.glade.h:6 #: ../data/glade/vcard_information_window.glade.h:8 -#, fuzzy msgid "<b>Country:</b>" -msgstr "<b>Sonidos</b>" +msgstr "<b>País:</b>"
#: ../data/glade/profile_window.glade.h:7 #: ../data/glade/vcard_information_window.glade.h:9 -#, fuzzy msgid "<b>Department:</b>" -msgstr "Departamento:" +msgstr "<b>Departamento:</b>"
#: ../data/glade/profile_window.glade.h:8 #: ../data/glade/vcard_information_window.glade.h:10 -#, fuzzy msgid "<b>E-Mail:</b>" -msgstr "Correo-e:" +msgstr "<b>Correo-e:</b>"
#: ../data/glade/profile_window.glade.h:9 #: ../data/glade/vcard_information_window.glade.h:11 -#, fuzzy msgid "<b>Extra Address:</b>" -msgstr "Segunda dirección:" +msgstr "<b>Segunda dirección:</b>"
#. Family Name #: ../data/glade/profile_window.glade.h:11 #: ../data/glade/vcard_information_window.glade.h:13 -#, fuzzy msgid "<b>Family:</b>" -msgstr "Familia:" +msgstr "<b>Familia:</b>"
#: ../data/glade/profile_window.glade.h:12 #: ../data/glade/vcard_information_window.glade.h:14 -#, fuzzy msgid "<b>Full Name</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Nombre completo</b>"
#. Given Name #: ../data/glade/profile_window.glade.h:14 #: ../data/glade/vcard_information_window.glade.h:16 -#, fuzzy msgid "<b>Given:</b>" -msgstr "<b>OpenPGP</b>" +msgstr "<b>Apodo:</b>"
#: ../data/glade/profile_window.glade.h:15 #: ../data/glade/vcard_information_window.glade.h:17 -#, fuzzy msgid "<b>Homepage:</b>" -msgstr "Página web:" +msgstr "<b>Página web:</b>"
#. Middle Name #: ../data/glade/profile_window.glade.h:17 #: ../data/glade/vcard_information_window.glade.h:20 -#, fuzzy msgid "<b>Middle:</b>" -msgstr "<b>Añadir una regla</b>" +msgstr "<b>Medio:</b>"
#: ../data/glade/profile_window.glade.h:18 #: ../data/glade/vcard_information_window.glade.h:21 -#, fuzzy msgid "<b>Name:</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Nombre:</b>"
#: ../data/glade/profile_window.glade.h:19 #: ../data/glade/vcard_information_window.glade.h:22 -#, fuzzy msgid "<b>Nickname:</b>" -msgstr "Alias:" +msgstr "<b>Alias:</b>"
#: ../data/glade/profile_window.glade.h:20 #: ../data/glade/vcard_information_window.glade.h:23 -#, fuzzy msgid "<b>Phone No.:</b>" -msgstr "Teléfono:" +msgstr "<b>Número de teléfono:</b>"
#: ../data/glade/profile_window.glade.h:21 #: ../data/glade/vcard_information_window.glade.h:24 -#, fuzzy msgid "<b>Position:</b>" -msgstr "<b>Condiciones</b>" +msgstr "<b>Posición:</b>"
#: ../data/glade/profile_window.glade.h:22 #: ../data/glade/vcard_information_window.glade.h:25 -#, fuzzy msgid "<b>Postal Code:</b>" -msgstr "Código postal:" +msgstr "<b>Código postal:</b>"
#. Prefix in Name #: ../data/glade/profile_window.glade.h:24 #: ../data/glade/vcard_information_window.glade.h:27 -#, fuzzy msgid "<b>Prefix:</b>" -msgstr "<b>Propiedades</b>" +msgstr "<b>Prefijo:</b>"
#: ../data/glade/profile_window.glade.h:25 #: ../data/glade/vcard_information_window.glade.h:29 ../src/vcard.py:307 -#, fuzzy msgid "<b>Role:</b>" -msgstr "<b>Registro</b>" +msgstr "<b>Rol:</b>"
#: ../data/glade/profile_window.glade.h:26 #: ../data/glade/vcard_information_window.glade.h:30 -#, fuzzy msgid "<b>State:</b>" -msgstr "<b>Configuración</b>" +msgstr "<b>Estado:</b>"
#: ../data/glade/profile_window.glade.h:27 #: ../data/glade/vcard_information_window.glade.h:32 -#, fuzzy msgid "<b>Street:</b>" -msgstr "<b>Configuración</b>" +msgstr "<b>Calle:</b>"
#. Suffix in Name #: ../data/glade/profile_window.glade.h:29 #: ../data/glade/vcard_information_window.glade.h:36 -#, fuzzy msgid "<b>Suffix:</b>" -msgstr "<b>Sonidos</b>" +msgstr "<b>Sufijo:</b>"
#. "About" is the text of a tab of vcard window #: ../data/glade/profile_window.glade.h:31 @@ -2417,8 +2354,8 @@
#. Remove group #. Remove -#: ../data/glade/remove_account_window.glade.h:4 ../src/roster_window.py:5146 -#: ../src/roster_window.py:5623 ../src/roster_window.py:5752 +#: ../data/glade/remove_account_window.glade.h:4 ../src/roster_window.py:5125 +#: ../src/roster_window.py:5603 ../src/roster_window.py:5732 msgid "_Remove" msgstr "_Eliminar"
@@ -2427,57 +2364,51 @@ msgstr "Pregunta para ver _su estado"
#: ../data/glade/roster_contact_context_menu.glade.h:2 -#, fuzzy msgid "Add Special _Notification..." -msgstr "Añadir _notificación especial" +msgstr "Añadir _notificación especial..."
#: ../data/glade/roster_contact_context_menu.glade.h:3 -#, fuzzy msgid "Assign Open_PGP Key..." -msgstr "Asignar clave OpenPGP" +msgstr "Asignar clave Open_PGP..."
#: ../data/glade/roster_contact_context_menu.glade.h:4 -#, fuzzy msgid "Edit _Groups..." -msgstr "Editar grupos" +msgstr "Editar _grupos..."
#. Execute Command #: ../data/glade/roster_contact_context_menu.glade.h:5 -#: ../src/roster_window.py:5695 +#: ../src/roster_window.py:5675 msgid "Execute Command..." msgstr "Ejecutar comando..."
#. Invite to #. Invite to Groupchat #: ../data/glade/roster_contact_context_menu.glade.h:6 -#: ../src/roster_window.py:5064 ../src/roster_window.py:5576 +#: ../src/roster_window.py:5043 ../src/roster_window.py:5556 msgid "In_vite to" msgstr "In_vitar a"
#: ../data/glade/roster_contact_context_menu.glade.h:7 -#, fuzzy msgid "Remo_ve" msgstr "_Eliminar"
#. Send Custom Status #: ../data/glade/roster_contact_context_menu.glade.h:8 -#: ../src/roster_window.py:5074 ../src/roster_window.py:5661 -#, fuzzy +#: ../src/roster_window.py:5053 ../src/roster_window.py:5641 msgid "Send Cus_tom Status" -msgstr "Envía XML personalizado" +msgstr "Envía estado personalizado"
#: ../data/glade/roster_contact_context_menu.glade.h:9 msgid "Send Single _Message..." msgstr "Enviar mensaje..."
#: ../data/glade/roster_contact_context_menu.glade.h:10 -#, fuzzy msgid "Send _File..." -msgstr "Enviar archivo" +msgstr "Enviar _archivo"
#: ../data/glade/roster_contact_context_menu.glade.h:11 msgid "Set Custom _Avatar..." -msgstr "" +msgstr "Definir _Avatar personalizado..."
#: ../data/glade/roster_contact_context_menu.glade.h:12 #: ../data/glade/zeroconf_contact_context_menu.glade.h:5 @@ -2489,10 +2420,10 @@ msgstr "Permitirle ver mi estado"
#: ../data/glade/roster_contact_context_menu.glade.h:15 -#: ../src/roster_window.py:5137 ../src/roster_window.py:5613 -#: ../src/roster_window.py:5742 +#: ../src/roster_window.py:5116 ../src/roster_window.py:5593 +#: ../src/roster_window.py:5722 msgid "_Block" -msgstr "" +msgstr "_Bloquear"
#: ../data/glade/roster_contact_context_menu.glade.h:16 msgid "_Forbid him/her to see my status" @@ -2500,43 +2431,38 @@
#: ../data/glade/roster_contact_context_menu.glade.h:18 msgid "_Ignore" -msgstr "" +msgstr "_Ignorar"
#: ../data/glade/roster_contact_context_menu.glade.h:19 #: ../data/glade/zeroconf_contact_context_menu.glade.h:7 -#, fuzzy msgid "_Manage Contact" -msgstr "Renombrar contacto" +msgstr "_Gestionar contacto"
#: ../data/glade/roster_contact_context_menu.glade.h:20 -#, fuzzy msgid "_Rename..." -msgstr "Renombrar" +msgstr "_Renombrar..."
#: ../data/glade/roster_contact_context_menu.glade.h:21 msgid "_Subscription" msgstr "_Subscripción"
#: ../data/glade/roster_contact_context_menu.glade.h:22 -#: ../src/roster_window.py:5131 ../src/roster_window.py:5607 -#: ../src/roster_window.py:5739 +#: ../src/roster_window.py:5110 ../src/roster_window.py:5587 +#: ../src/roster_window.py:5719 msgid "_Unblock" -msgstr "" +msgstr "_Desbloquear"
#: ../data/glade/roster_contact_context_menu.glade.h:23 -#, fuzzy msgid "_Unignore" -msgstr "nueve" +msgstr "_No ignorar"
#: ../data/glade/roster_window.glade.h:1 -#, fuzzy msgid "Add _Contact..." msgstr "_Añadir contacto..."
#: ../data/glade/roster_window.glade.h:2 -#, fuzzy msgid "Fea_tures" -msgstr "Características de los servidores" +msgstr "Carac_terísticas"
#: ../data/glade/roster_window.glade.h:3 msgid "File _Transfers" @@ -2563,14 +2489,12 @@ msgstr "Mostrar contactos desconectados"
#: ../data/glade/roster_window.glade.h:11 -#, fuzzy msgid "Show _roster" msgstr "Mostrar _roster"
#: ../data/glade/roster_window.glade.h:12 -#, fuzzy msgid "_Accounts" -msgstr "Cuentas" +msgstr "_Cuentas"
#. Make sure the character after "_" is not M/m (conflicts with Alt+M that is supposed to show the Emoticon Selector) #: ../data/glade/roster_window.glade.h:13 @@ -2582,13 +2506,13 @@ msgid "_Contents" msgstr "_Contenidos"
-#: ../data/glade/roster_window.glade.h:17 ../src/disco.py:1351 +#: ../data/glade/roster_window.glade.h:17 ../src/disco.py:1352 msgid "_Edit" msgstr "_Editar"
#: ../data/glade/roster_window.glade.h:18 msgid "_FAQ" -msgstr "" +msgstr "_FAQ"
#: ../data/glade/roster_window.glade.h:19 msgid "_Help" @@ -2603,35 +2527,31 @@ msgstr "_Salir"
#: ../data/glade/roster_window.glade.h:23 -#, fuzzy msgid "_Send Single Message..." -msgstr "Enviar mensaje..." +msgstr "E_nviar mensaje..."
#: ../data/glade/roster_window.glade.h:25 msgid "_View" msgstr "_Ver"
#: ../data/glade/search_window.glade.h:1 -#, fuzzy msgid "Please wait while retrieving search form..." -msgstr "Por favor, espera mientras se solicita la lista de comandos..." +msgstr "Por favor, espera mientras se solicita el formulario de búsqueda..."
#: ../data/glade/search_window.glade.h:2 msgid "Search" msgstr "Buscar"
#: ../data/glade/search_window.glade.h:3 -#, fuzzy msgid "_Add contact" msgstr "Añadir un _Contacto"
#. Information -#: ../data/glade/search_window.glade.h:4 ../src/roster_window.py:5764 -#, fuzzy +#: ../data/glade/search_window.glade.h:4 ../src/roster_window.py:5744 msgid "_Information" -msgstr "Información" +msgstr "_Información"
-#: ../data/glade/search_window.glade.h:5 ../src/disco.py:1209 +#: ../data/glade/search_window.glade.h:5 ../src/disco.py:1210 msgid "_Search" msgstr "_Buscar"
@@ -2657,7 +2577,7 @@
#: ../data/glade/single_message_window.glade.h:1 msgid "0" -msgstr "" +msgstr "0"
#: ../data/glade/single_message_window.glade.h:2 msgid "From:" @@ -2700,17 +2620,15 @@ msgstr "Au_torizar"
#: ../data/glade/subscription_request_window.glade.h:2 -#, fuzzy msgid "Authorize contact so he or she can know when you're connected" msgstr "Autorizar contacto para que pueda saber cuándo estás conectado"
#: ../data/glade/subscription_request_window.glade.h:3 -#, fuzzy msgid "" "Deny authorization from contact so he or she cannot know when you're " "connected" msgstr "" -"Denegar autorización de contacto para que no pueda saber cuándo estás " +"Denegar autorización al contacto para que no pueda saber cuándo estás " "conectado"
#: ../data/glade/subscription_request_window.glade.h:4 @@ -2723,20 +2641,19 @@
#: ../data/glade/synchronise_contacts_dialog.glade.h:1 msgid "Select the account with which to synchronise" -msgstr "" +msgstr "Selecciona la cuenta con la que sincronizar"
#: ../data/glade/synchronise_select_account_dialog.glade.h:1 msgid "Select the account with which you want to synchronise" -msgstr "" +msgstr "Selecciona la cuenta con la que quieres sincronizar"
#: ../data/glade/synchronise_select_contacts_dialog.glade.h:1 -#, fuzzy msgid "Select the contacts you want to synchronise" -msgstr "JID del contacto con el que quieres conversar" +msgstr "Selecciona los contactos que quieres sincronizar"
#: ../data/glade/synchronise_select_contacts_dialog.glade.h:2 msgid "Synchronise : select contacts" -msgstr "" +msgstr "Sincronizar: seleccionar contactos"
#: ../data/glade/systray_context_menu.glade.h:1 msgid "Mute Sounds" @@ -2764,49 +2681,41 @@
#. Given Name #: ../data/glade/vcard_information_window.glade.h:3 -#, fuzzy msgid "<b>Ask:</b>" -msgstr "<b>Acciones</b>" +msgstr "<b>Pregunta:</b>"
#: ../data/glade/vcard_information_window.glade.h:6 -#, fuzzy msgid "<b>Client:</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Cliente:</b>"
#: ../data/glade/vcard_information_window.glade.h:18 -#, fuzzy msgid "<b>Jabber ID:</b>" -msgstr "ID de Jabber:" +msgstr "<b>ID de Jabber:</b>"
#: ../data/glade/vcard_information_window.glade.h:28 -#, fuzzy msgid "<b>Resource:</b>" -msgstr "Recurso:" +msgstr "<b>Recurso:</b>"
#: ../data/glade/vcard_information_window.glade.h:31 -#, fuzzy msgid "<b>Status:</b>" -msgstr "<b>Configuración</b>" +msgstr "<b>Estado:</b>"
#. Family Name #: ../data/glade/vcard_information_window.glade.h:34 -#, fuzzy msgid "<b>Subscription:</b>" -msgstr "<b>Descripción</b>" +msgstr "<b>Suscripción:</b>"
#: ../data/glade/vcard_information_window.glade.h:37 -#, fuzzy msgid "<b>System:</b>" -msgstr "<b>Personalizado</b>" +msgstr "<b>Sistema:</b>"
#: ../data/glade/vcard_information_window.glade.h:40 msgid "Comments" msgstr "Comentarios"
#: ../data/glade/vcard_information_window.glade.h:41 -#, fuzzy msgid "Configured avatar:" -msgstr "Configurar el salón" +msgstr "Avatar configurado:"
#: ../data/glade/vcard_information_window.glade.h:42 #: ../data/glade/zeroconf_information_window.glade.h:1 @@ -2822,9 +2731,8 @@ msgstr "Más"
#: ../data/glade/vcard_information_window.glade.h:48 -#, fuzzy msgid "User avatar:" -msgstr "Elegir avatar" +msgstr "Avatar del usuario:"
#: ../data/glade/xml_console_window.glade.h:1 msgid "<b>Jabber Traffic</b>" @@ -2862,13 +2770,13 @@
#. Edit Groups #: ../data/glade/zeroconf_contact_context_menu.glade.h:3 -#: ../src/roster_window.py:5596 +#: ../src/roster_window.py:5576 msgid "Edit _Groups" msgstr "Editar grupos"
#. Rename #: ../data/glade/zeroconf_contact_context_menu.glade.h:8 -#: ../src/roster_window.py:5722 +#: ../src/roster_window.py:5702 msgid "_Rename" msgstr "Renombrar"
@@ -2897,55 +2805,51 @@ msgstr "Modificar cuenta"
#: ../src/adhoc_commands.py:264 -#, fuzzy msgid "Cancel confirmation" -msgstr "Información" +msgstr "Cancelar información"
#: ../src/adhoc_commands.py:265 msgid "" "You are in process of executing command. Do you really want to cancel it?" -msgstr "" +msgstr "Hay un comando en ejecución. ¿Realmente quieres cancelarlo?"
#: ../src/adhoc_commands.py:304 msgid "Service changed the session identifier." -msgstr "" +msgstr "El servicio cambió el identificador de la sesión"
#: ../src/adhoc_commands.py:317 msgid "Service sent malformed data" -msgstr "" +msgstr "El servicio envió información malformada"
#. when stanza doesn't have error description #: ../src/adhoc_commands.py:398 msgid "Service returned an error." -msgstr "" +msgstr "El servicio devolvió un error"
#. For i18n #: ../src/advanced.py:97 -#, fuzzy msgid "Activated" -msgstr "Activo" +msgstr "Activado"
#: ../src/advanced.py:97 -#, fuzzy msgid "Deactivated" -msgstr "Inactivo" +msgstr "Desactivado"
#: ../src/advanced.py:99 msgid "Boolean" -msgstr "" +msgstr "Booleano"
#: ../src/advanced.py:100 -#, fuzzy msgid "Integer" -msgstr "En el grupo" +msgstr "Entero"
#: ../src/advanced.py:101 msgid "Text" msgstr "Texto"
-#: ../src/advanced.py:102 +#: ../src/advanced.py:102 ../src/chat_control.py:763 msgid "Color" -msgstr "" +msgstr "Color"
#: ../src/advanced.py:113 msgid "Preference Name" @@ -2973,330 +2877,343 @@ #. #!/bin/sh #. LANG=$(for i in *.po; do j=${i/.po/}; echo -n "_('"$j"')":" '"$j"', " ; done) #. echo "{_('en'):'en'",$LANG"}" -#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "English" msgstr "Inglés"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Belarusian" -msgstr "Búlgaro" +msgstr "Bieloruso"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Bulgarian" msgstr "Búlgaro"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Breton" -msgstr "Británico" +msgstr "Bretón"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Czech" msgstr "Checo"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "German" msgstr "Alemán"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Greek" msgstr "Griego"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "British" msgstr "Británico"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Esperanto" msgstr "Esperanto"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Spanish" msgstr "Español"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Basque" msgstr "Euskera"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "French" msgstr "Francés"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Croatian" msgstr "Croata"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Italian" msgstr "Italiano"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Norwegian (b)" -msgstr "Noruego b" +msgstr "Noruego (b)"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Dutch" msgstr "Holandés"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Norwegian" msgstr "Noruego"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Polish" msgstr "Polaco"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Portuguese" msgstr "Portugués"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Brazilian Portuguese" msgstr "Portugués de Brasil"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Russian" msgstr "Ruso"
-#: ../src/chat_control.py:68 -#, fuzzy +#: ../src/chat_control.py:67 msgid "Serbian" -msgstr "Alemán" +msgstr "Serbio"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Slovak" msgstr "Eslovaco"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Swedish" msgstr "Sueco"
-#: ../src/chat_control.py:68 +#: ../src/chat_control.py:67 msgid "Chinese (Ch)" msgstr "Chino (Ch)"
-#: ../src/chat_control.py:332 +#: ../src/chat_control.py:335 msgid "Spelling language" msgstr "Lenguaje del corrector"
#. we are not connected -#: ../src/chat_control.py:355 ../src/chat_control.py:549 +#: ../src/chat_control.py:358 ../src/chat_control.py:546 msgid "A connection is not available" msgstr "No hay disponible una conexión"
-#: ../src/chat_control.py:356 ../src/chat_control.py:550 +#: ../src/chat_control.py:359 ../src/chat_control.py:547 msgid "Your message can not be sent until you are connected." msgstr "Tu mensaje no será enviado mientras no estés conectado."
-#: ../src/chat_control.py:1147 ../src/chat_control.py:1543 +#: ../src/chat_control.py:745 #, fuzzy +msgid "Underline" +msgstr "Indefinido" + +#: ../src/chat_control.py:746 +#, fuzzy +msgid "Strike" +msgstr "Servicio" + +#: ../src/chat_control.py:769 +#, fuzzy +msgid "Font" +msgstr "_Fuente:" + +#: ../src/chat_control.py:778 +#, fuzzy +msgid "Clear formating" +msgstr "Información" + +#: ../src/chat_control.py:1212 ../src/chat_control.py:1614 msgid "GPG encryption enabled" -msgstr "Encriptación activada" +msgstr "Cifrado GPG habilitado"
#. Add to roster #. add_to_roster_menuitem -#: ../src/chat_control.py:1169 ../src/chat_control.py:2037 -#: ../src/common/contacts.py:113 ../src/common/helpers.py:73 -#: ../src/common/helpers.py:303 ../src/conversation_textview.py:845 -#: ../src/dialogs.py:913 ../src/gajim.py:1633 ../src/normal_control.py:483 -#: ../src/roster_window.py:916 ../src/roster_window.py:1542 -#: ../src/roster_window.py:1544 ../src/roster_window.py:1880 -#: ../src/roster_window.py:3111 ../src/roster_window.py:3135 -#: ../src/roster_window.py:5258 ../src/roster_window.py:5442 +#: ../src/chat_control.py:1241 ../src/chat_control.py:2108 +#: ../src/common/contacts.py:113 ../src/common/helpers.py:72 +#: ../src/common/helpers.py:302 ../src/conversation_textview.py:860 +#: ../src/dialogs.py:903 ../src/gajim.py:980 ../src/gajim.py:1640 +#: ../src/normal_control.py:483 ../src/roster_window.py:911 +#: ../src/roster_window.py:1537 ../src/roster_window.py:1539 +#: ../src/roster_window.py:1870 ../src/roster_window.py:3092 +#: ../src/roster_window.py:3115 ../src/roster_window.py:5237 +#: ../src/roster_window.py:5422 msgid "Not in Roster" msgstr "no está en el roster"
-#: ../src/chat_control.py:1301 ../src/tooltips.py:627 -#, fuzzy +#: ../src/chat_control.py:1373 ../src/tooltips.py:627 msgid "Unknown Artist" -msgstr "tipo no conocido %s " +msgstr "Artista desconocido"
-#: ../src/chat_control.py:1303 ../src/tooltips.py:632 -#, fuzzy +#: ../src/chat_control.py:1375 ../src/tooltips.py:632 msgid "Unknown Title" -msgstr "tipo no conocido %s " +msgstr "Título desconocido"
-#: ../src/chat_control.py:1305 ../src/tooltips.py:637 -#, fuzzy +#: ../src/chat_control.py:1377 ../src/tooltips.py:637 msgid "Unknown Source" -msgstr "tipo no conocido %s " +msgstr "Recurso desconocido"
-#: ../src/chat_control.py:1309 ../src/tooltips.py:639 +#: ../src/chat_control.py:1381 ../src/tooltips.py:639 #, python-format msgid "" "<b>"%(title)s"</b> by <i>%(artist)s</i>\n" "from <i>%(source)s</i>" msgstr "" +"<b>"%(title)s"</b> por <i>%(artist)s</i>\n" +"de <i>%(source)s</i>"
-#: ../src/chat_control.py:1314 -#, fuzzy, python-format +#: ../src/chat_control.py:1386 +#, python-format msgid "" "%(title)s by %(artist)s\n" "from %(source)s" -msgstr ""%(title)s" por %(artist)s" +msgstr "" +"%(title)s por %(artist)s\n" +"de %(source)s"
-#: ../src/chat_control.py:1438 +#: ../src/chat_control.py:1509 #, python-format msgid "%(nickname)s from group chat %(room_name)s" msgstr "%(nickname)s del salón %(room_name)s"
#. No key assigned nor a key is used by remote contact -#: ../src/chat_control.py:1523 ../src/dialogs.py:3920 +#: ../src/chat_control.py:1594 ../src/dialogs.py:3913 msgid "No GPG key assigned" -msgstr "" +msgstr "Ninguna clave GPG asignada"
-#: ../src/chat_control.py:1524 +#: ../src/chat_control.py:1595 msgid "" "No GPG key is assigned to this contact. So you cannot encrypt messages with " "GPG." msgstr "" +"No se ha asignado ninguna clave GPG a este contacto. Por tanto no podrás " +"cifrar mensajes con GPG."
-#: ../src/chat_control.py:1533 -#, fuzzy +#: ../src/chat_control.py:1604 msgid "GPG encryption disabled" -msgstr "Encriptación desactivada" +msgstr "Cifrado GPG deshabilitado"
-#: ../src/chat_control.py:1558 +#: ../src/chat_control.py:1629 msgid "Session WILL be logged" -msgstr "" +msgstr "La sesión SERÁ registrada"
-#: ../src/chat_control.py:1560 +#: ../src/chat_control.py:1631 msgid "Session WILL NOT be logged" -msgstr "" +msgstr "La NO SERÁ registrada"
#. encryption %s active -#: ../src/chat_control.py:1574 +#: ../src/chat_control.py:1645 msgid "is" -msgstr "" +msgstr "es"
-#: ../src/chat_control.py:1574 +#: ../src/chat_control.py:1645 msgid "is NOT" -msgstr "" +msgstr "NO es"
#. chat session %s be logged -#: ../src/chat_control.py:1576 +#: ../src/chat_control.py:1647 msgid "will" -msgstr "" +msgstr "será"
-#: ../src/chat_control.py:1576 +#: ../src/chat_control.py:1647 msgid "will NOT" -msgstr "" +msgstr "no será"
#. About encrypted chat session -#: ../src/chat_control.py:1580 -#, fuzzy +#: ../src/chat_control.py:1651 msgid "and authenticated" -msgstr "Usar autentificación" +msgstr "y autentificado"
#. About encrypted chat session -#: ../src/chat_control.py:1584 -#, fuzzy +#: ../src/chat_control.py:1655 msgid "and NOT authenticated" -msgstr "Usar autentificación" +msgstr "y NO autenticado"
#. status will become 'is' or 'is not', authentificaed will become #. 'and authentificated' or 'and not authentificated', logged will become #. 'will' or 'will not' -#: ../src/chat_control.py:1590 +#: ../src/chat_control.py:1661 #, python-format msgid "" "%(type)s encryption %(status)s active %(authenticated)s.\n" "Your chat session %(logged)s be logged." msgstr "" +"%(type)s cifrado %(status)s activo %(authenticated)s.\n" +"Tu sesión %(logged)s guardada."
-#: ../src/chat_control.py:1638 +#: ../src/chat_control.py:1709 msgid "Command not supported for zeroconf account." -msgstr "" +msgstr "Comando no soportado por la cuenta zeroconf."
-#: ../src/chat_control.py:1649 ../src/groupchat_control.py:1653 +#: ../src/chat_control.py:1720 ../src/groupchat_control.py:1654 #, python-format msgid "Commands: %s" msgstr "Comandos: %s"
-#: ../src/chat_control.py:1652 ../src/groupchat_control.py:1667 +#: ../src/chat_control.py:1723 ../src/groupchat_control.py:1668 #, python-format msgid "Usage: /%s, clears the text window." msgstr "Uso: /%s, limpia de texto la ventana."
-#: ../src/chat_control.py:1655 ../src/groupchat_control.py:1672 +#: ../src/chat_control.py:1726 ../src/groupchat_control.py:1673 #, python-format msgid "Usage: /%s, hide the chat buttons." msgstr "Uso: /%s, oculta los botones de conversación."
-#: ../src/chat_control.py:1658 ../src/groupchat_control.py:1688 -#, fuzzy, python-format +#: ../src/chat_control.py:1729 ../src/groupchat_control.py:1689 +#, python-format msgid "" "Usage: /%(command)s <action>, sends action to the current group chat. Use " "third person. (e.g. /%(command)s explodes.)" msgstr "" -"Uso: /%s <acción>, envía una acción al grupo de charla actual. Usa la " -"tercera persona. (p.ej. /%s grita.)" +"Uso: /%(command)s <action>, envía action al actual grupo de charla. Usa una " +"tercera persona (p.ej. /%(command)s explota.)"
-#: ../src/chat_control.py:1663 -#, fuzzy, python-format +#: ../src/chat_control.py:1734 +#, python-format msgid "Usage: /%s, sends a ping to the contact" -msgstr "Uso: /%s, cambia la ventana de salón a modo compacto." +msgstr "Uso: /%s, envía un ping al contacto"
-#: ../src/chat_control.py:1666 -#, fuzzy, python-format +#: ../src/chat_control.py:1737 +#, python-format msgid "Usage: /%s, send the message to the contact" -msgstr "Uso: /%s, oculta los botones de conversación." +msgstr "Uso: /%s, envía el mensaje al contacto"
-#: ../src/chat_control.py:1669 ../src/groupchat_control.py:1711 +#: ../src/chat_control.py:1740 ../src/groupchat_control.py:1712 #, python-format msgid "No help info for /%s" msgstr "No hay información de ayuda para /%s"
-#: ../src/chat_control.py:1800 +#: ../src/chat_control.py:1872 msgid "Session negotiation cancelled" -msgstr "" +msgstr "La negociación de la sesión ha sido cancelada"
-#: ../src/chat_control.py:1807 -#, fuzzy +#: ../src/chat_control.py:1879 msgid "This session is encrypted" -msgstr "[Este mensaje está encriptado]" +msgstr "Esta sesión está cifrada"
-#: ../src/chat_control.py:1810 +#: ../src/chat_control.py:1882 msgid " and WILL be logged" -msgstr "" +msgstr " y SERÁ registrada"
-#: ../src/chat_control.py:1812 +#: ../src/chat_control.py:1884 msgid " and WILL NOT be logged" -msgstr "" +msgstr " y NO SERÁ registrada"
-#: ../src/chat_control.py:1817 +#: ../src/chat_control.py:1889 msgid "" "Remote contact's identity not verified. Click the shield button for more " "details." msgstr "" +"No se verificó la identidad del contacto remoto. Click en el botón del " +"escudo para más detalles."
-#: ../src/chat_control.py:1819 -#, fuzzy +#: ../src/chat_control.py:1891 msgid "E2E encryption disabled" -msgstr "Encriptación desactivada" +msgstr "Cifrado E2E desactivado"
-#: ../src/chat_control.py:1848 ../src/chat_control.py:1861 -#, fuzzy +#: ../src/chat_control.py:1919 ../src/chat_control.py:1932 msgid "The following message was NOT encrypted" -msgstr "[Este mensaje está encriptado]" +msgstr "El siguiente mensaje NO fue cifrado"
-#: ../src/chat_control.py:1854 -#, fuzzy +#: ../src/chat_control.py:1925 msgid "The following message was encrypted" -msgstr "[Este mensaje está encriptado]" +msgstr "El siguiente mensaje fue cifrado"
#. %s is being replaced in the code with JID -#: ../src/chat_control.py:2225 +#: ../src/chat_control.py:2296 #, python-format msgid "You just received a new message from "%s"" msgstr "Acabas de recibir un nuevo mensaje de "%s""
-#: ../src/chat_control.py:2226 +#: ../src/chat_control.py:2297 msgid "" "If you close this tab and you have history disabled, this message will be " "lost." @@ -3304,138 +3221,129 @@ "Si cierras esta pestaña y tienes el regstro desactivado, este mensaje se " "perderá."
-#: ../src/chat_control.py:2376 ../src/common/connection_handlers.py:1859 -#: ../src/common/connection_handlers.py:1905 -#: ../src/common/connection_handlers.py:2093 -#: ../src/common/connection_handlers.py:2219 ../src/common/connection.py:1229 -#: ../src/gajim.py:170 +#: ../src/chat_control.py:2445 ../src/common/connection_handlers.py:1850 +#: ../src/common/connection_handlers.py:1896 +#: ../src/common/connection_handlers.py:2084 +#: ../src/common/connection_handlers.py:2217 ../src/common/connection.py:1260 +#: ../src/gajim.py:178 msgid "Database Error" -msgstr "" +msgstr "Error en la base de datos"
-#: ../src/chat_control.py:2377 +#: ../src/chat_control.py:2446 #, python-format msgid "" "The database file (%s) cannot be read. Try to repair it or remove it (all " "history will be lost)." msgstr "" +"No se pudo leer el archivo de base de datos (%s). Intenta repararlo o " +"elimínalo (se perderá todo el histórico)."
-#: ../src/chat_control.py:2608 -#, fuzzy, python-format +#: ../src/chat_control.py:2682 +#, python-format msgid "%(name)s is now %(status)s" -msgstr "%(nick)s está ahora %(status)s" +msgstr "%(name)s está ahora %(status)s"
-#: ../src/common/check_paths.py:43 +#: ../src/common/check_paths.py:44 msgid "creating logs database" msgstr "creando base de datos de registros"
-#: ../src/common/check_paths.py:110 ../src/common/check_paths.py:121 -#: ../src/common/check_paths.py:128 -#, fuzzy, python-format +#: ../src/common/check_paths.py:111 ../src/common/check_paths.py:122 +#: ../src/common/check_paths.py:129 +#, python-format msgid "%s is a file but it should be a directory" msgstr "%s es un archivo pero podría ser un directorio"
-#: ../src/common/check_paths.py:111 ../src/common/check_paths.py:122 -#: ../src/common/check_paths.py:129 ../src/common/check_paths.py:137 +#: ../src/common/check_paths.py:112 ../src/common/check_paths.py:123 +#: ../src/common/check_paths.py:130 ../src/common/check_paths.py:138 msgid "Gajim will now exit" msgstr "Gajim se cerrará ahora"
-#: ../src/common/check_paths.py:136 -#, fuzzy, python-format +#: ../src/common/check_paths.py:137 +#, python-format msgid "%s is a directory but should be a file" msgstr "%s es un directorio pero podría ser un archivo"
-#: ../src/common/check_paths.py:152 +#: ../src/common/check_paths.py:153 #, python-format msgid "creating %s directory" msgstr "creando directorio %s"
#: ../src/common/commands.py:80 -#, fuzzy msgid "Change status information" -msgstr "Información" +msgstr "Cambiar información de estado"
#: ../src/common/commands.py:93 -#, fuzzy msgid "Change status" -msgstr "Contacto cambió su estado" +msgstr "Cambiar estado"
#: ../src/common/commands.py:94 msgid "Set the presence type and description" -msgstr "" +msgstr "Definir la presencia y una descripción"
#: ../src/common/commands.py:100 -#, fuzzy msgid "Free for chat" msgstr "Libre para hablar"
#: ../src/common/commands.py:101 -#, fuzzy msgid "Online" -msgstr "nueve" +msgstr "En línea"
#: ../src/common/commands.py:103 msgid "Extended away" -msgstr "" +msgstr "Ausencia extendida"
#: ../src/common/commands.py:104 -#, fuzzy msgid "Do not disturb" msgstr "No molestar."
#: ../src/common/commands.py:105 msgid "Offline - disconnect" -msgstr "" +msgstr "Desconectado"
#: ../src/common/commands.py:110 -#, fuzzy msgid "Presence description:" -msgstr "Descripción del error..." +msgstr "Descripción de la presencia:"
#: ../src/common/commands.py:145 msgid "The status has been changed." -msgstr "" +msgstr "Se ha cambiado el estado."
#: ../src/common/commands.py:177 ../src/common/commands.py:201 -#, fuzzy msgid "Leave Groupchats" -msgstr "Nuevo grupo de charla" +msgstr "Abandonar grupos de charla"
#: ../src/common/commands.py:191 -#, fuzzy, python-format +#, python-format msgid "%(nickname)s on %(room_jid)s" -msgstr "%(nickname)s del salón %(room_name)s" +msgstr "%(nickname)s en %(room_jid)s"
#: ../src/common/commands.py:195 -#, fuzzy msgid "You have not joined a groupchat." -msgstr "%s ha entrado en el grupo de charla" +msgstr "No has entrado al grupo de charla"
#: ../src/common/commands.py:202 msgid "Choose the groupchats you want to leave" -msgstr "" +msgstr "Elige los grupos de conversación que quieres abandonar"
#. Make special context menu if group is Groupchats #: ../src/common/commands.py:206 ../src/common/contacts.py:94 -#: ../src/common/helpers.py:73 ../src/roster_window.py:788 -#: ../src/roster_window.py:1546 ../src/roster_window.py:1548 -#: ../src/roster_window.py:5034 -#, fuzzy +#: ../src/common/helpers.py:72 ../src/roster_window.py:785 +#: ../src/roster_window.py:1541 ../src/roster_window.py:1543 +#: ../src/roster_window.py:5013 msgid "Groupchats" msgstr "Grupos de charla"
#: ../src/common/commands.py:247 -#, fuzzy msgid "You left the following groupchats:" -msgstr "Ya estás en el salón %s" +msgstr "Abandonaste los siguientes grupos de charla:"
#: ../src/common/commands.py:259 -#, fuzzy msgid "Forward unread messages" -msgstr "%d mensaje sin leer" +msgstr "Reenviar mensajes sin leer"
#: ../src/common/commands.py:279 msgid "All unread messages have been forwarded." -msgstr "" +msgstr "Todos los mensajes sin leer han sido reenviados"
#: ../src/common/config.py:82 msgid "Use D-Bus and Notification-Daemon to show notifications" @@ -3446,26 +3354,28 @@ msgstr "Tiempo en minutos, después de haber cambiado a ausente"
#: ../src/common/config.py:88 -#, fuzzy msgid "$S (Away as a result of being idle more than $T min)" -msgstr "Ausente por inactividad" +msgstr "$S (Ausente por estar inactivo más de $T minutos)"
#: ../src/common/config.py:88 msgid "$S will be replaced by current status message, $T by autoaway time." msgstr "" +"$S será reemplazado por el mensaje de estado actual, $T por tiempo de auto-" +"ausencia"
#: ../src/common/config.py:90 msgid "Time in minutes, after which your status changes to not available." msgstr "Tiempo en minutos, después de haber cambiado a no disponible"
#: ../src/common/config.py:91 -#, fuzzy msgid "$S (Not available as a result of being idle more than $T min)" -msgstr "No disponible por inactividad" +msgstr "$S (No disponible por estar inactivo más de $T minutos)"
#: ../src/common/config.py:91 msgid "$S will be replaced by current status message, $T by autoxa time." msgstr "" +"$S será reemplazado por el actual mensaje de estado, $T por tiempo de auto-" +"ausencia extendida"
# duda sobre collapsed #: ../src/common/config.py:111 @@ -3475,12 +3385,11 @@ "Lista (separada por espacios) de filas (cuentas y grupos) que están caídas."
#. sorted alphanum -#: ../src/common/config.py:112 ../src/common/config.py:448 -#: ../src/common/optparser.py:215 ../src/common/optparser.py:433 -#: ../src/common/optparser.py:467 ../src/gajim.py:3114 -#, fuzzy +#: ../src/common/config.py:112 ../src/common/config.py:461 +#: ../src/common/optparser.py:217 ../src/common/optparser.py:435 +#: ../src/common/optparser.py:469 ../src/gajim.py:3128 msgid "default" -msgstr "Por defecto" +msgstr "por defecto"
#: ../src/common/config.py:116 msgid "Enable link-local/zeroconf messaging" @@ -3506,6 +3415,9 @@ "to disable fuzzyclock. 1 is the most precise clock, 4 the least precise one. " "This is used only if print_time is 'sometimes'." msgstr "" +"Imprime la hora en las conversaciones usando reloj hablado. El valor va de 1 " +"a 4, o 0 para deshabilitar el reloj hablado. 1 es el reloj más preciso, 4 el " +"menos. Sólo se usa si print_time está definido como 'sometimes'."
#: ../src/common/config.py:124 msgid "Treat * / _ pairs as possible formatting characters." @@ -3519,14 +3431,15 @@ "* *"
#: ../src/common/config.py:128 -#, fuzzy 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 "" -"Usa la sintaxis ReStructured para HTML, mas el formateo ascii si está " -"seleccionado. (Si quieres usarlo, instala docutils)" +"Usa marcado de texto ReStructured para enviar HTML, mas formateado ascii si " +"está seleccionado. Para la sintaxis, mira http://docutils.sourceforge.net/" +"docs/ref/rst/restructuredtext.html (If you want to use this, install " +"docutils)"
#: ../src/common/config.py:137 msgid "" @@ -3544,26 +3457,30 @@ "Carácter a proponer para añadir tras el alias deseado cuando el alias " "deseado está siendo usado por alguien en el grupo de charla."
-#: ../src/common/config.py:161 +#: ../src/common/config.py:165 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 "" +"Esta opción te permite personalizar la marca horaria de las conversaciones. " +"Por ejemplo "[%H:%M] " mostrará "[hour:minute] ". Mira la documentación " +"de Python acerca de strftime y para más detalles: http://docs.python.org/lib/" +"module-time.html"
-#: ../src/common/config.py:162 +#: ../src/common/config.py:166 msgid "Characters that are printed before the nickname in conversations" -msgstr "" +msgstr "Los caracteres que se imprimen antes del alias en las conversaciones"
-#: ../src/common/config.py:163 +#: ../src/common/config.py:167 msgid "Characters that are printed after the nickname in conversations" -msgstr "" +msgstr "Los caracteres que se imprimen después del alias en las conversaciones"
-#: ../src/common/config.py:168 +#: ../src/common/config.py:172 msgid "Add * and [n] in roster title?" msgstr "¿Añadir * y [n] en el título del roster?"
-#: ../src/common/config.py:169 +#: ../src/common/config.py:173 msgid "" "How many lines to remember from previous conversation when a chat tab/window " "is reopened." @@ -3571,12 +3488,12 @@ "Cuántas líneas recordar de la última conversación cuando se vuelve a abrir " "una pestaña/ventana de conversación"
-#: ../src/common/config.py:170 +#: ../src/common/config.py:174 msgid "How many minutes should last lines from previous conversation last." msgstr "" "Cuántos minutos debe durar la última línea de la conversación anterior."
-#: ../src/common/config.py:171 +#: ../src/common/config.py:175 msgid "" "Send message on Ctrl+Enter and with Enter make new line (Mirabilis ICQ " "Client default behaviour)." @@ -3584,11 +3501,11 @@ "Envía un mensaje con Ctrl+Intro y con Intro hace un nuevo renglón (Al estilo " "del cliente Mirabilis ICQ)"
-#: ../src/common/config.py:173 +#: ../src/common/config.py:177 msgid "How many lines to store for Ctrl+KeyUP." msgstr "Cuántas líneas almacenar para Ctrl+Arriba."
-#: ../src/common/config.py:176 +#: ../src/common/config.py:180 #, python-format msgid "" "Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' " @@ -3597,12 +3514,12 @@ "URL personalizada con %s donde %s es la palabra/frase o 'WIKTIONARY' que " "significa usar wiktionary"
-#: ../src/common/config.py:179 +#: ../src/common/config.py:183 msgid "If checked, Gajim can be controlled remotely using gajim-remote." msgstr "" "Si está marcado, Gajim puede ser controlado remotamente usando gajim-remote."
-#: ../src/common/config.py:180 +#: ../src/common/config.py:184 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 " @@ -3614,13 +3531,13 @@ "puesto en False y sincronicen con el estado global) basados en el estado de " "la conexión de la red."
-#: ../src/common/config.py:181 +#: ../src/common/config.py:185 msgid "" "Sent chat state notifications. Can be one of all, composing_only, disabled." msgstr "" "Notificaciones de estado enviadas. Puede ser all, composing_only o disabled."
-#: ../src/common/config.py:182 +#: ../src/common/config.py:186 msgid "" "Displayed chat state notifications in chat windows. Can be one of all, " "composing_only, disabled." @@ -3628,7 +3545,7 @@ "Las notificaciones de estado mostradas en las ventanas de conversación. " "Pueden ser all, composing_only o disabled."
-#: ../src/common/config.py:184 +#: ../src/common/config.py:188 msgid "" "When not printing time for every message (print_time==sometimes), print it " "every x minutes." @@ -3636,11 +3553,11 @@ "Cuando no se muestra la hora en cada mensaje (print_time==sometimes), la " "muestra cada x minutos."
-#: ../src/common/config.py:185 +#: ../src/common/config.py:189 msgid "Ask before closing a group chat tab/window." msgstr "Preguntar antes de cerrar una ventana/petaña de salón de charla."
-#: ../src/common/config.py:186 +#: ../src/common/config.py:190 msgid "" "Always ask before closing group chat tab/window in this space separated list " "of group chat jids." @@ -3648,7 +3565,7 @@ "Preguntar siempre antes de cerrar la ventana/pestaña de un grupo de charla " "en esta lista (separada por espacios) de ID de Jabber de grupos de charla."
-#: ../src/common/config.py:187 +#: ../src/common/config.py:191 msgid "" "Never ask before closing group chat tab/window in this space separated list " "of group chat jids." @@ -3657,63 +3574,67 @@ "conversación en esta lista (separada por espacios) de ID de Jabber de grupos " "de charla."
-#: ../src/common/config.py:190 -#, fuzzy +#: ../src/common/config.py:194 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 "" -"Se elimina el host que enviamos para la transferencia de un archivo en caso " -"de traducción de la dirección o redirección de puertos." +"Lista de hosts que enviamos separados por comas, además de las interfaces " +"locales, para las transferencias de archivos en caso redirección de puerto o " +"interpretación de la dirección."
-#: ../src/common/config.py:192 +#: ../src/common/config.py:196 msgid "IEC standard says KiB = 1024 bytes, KB = 1000 bytes." msgstr "Según el estándar IEC, KiB = 1024 bytes, KB = 1000 bytes."
-#: ../src/common/config.py:194 +#: ../src/common/config.py:198 msgid "Notify of events in the system trayicon." msgstr "Notifica eventos en el área de notificación."
-#: ../src/common/config.py:200 +#: ../src/common/config.py:204 msgid "Show tab when only one conversation?" msgstr "¿Mostrar pestaña cuando sólo hay una conversación?"
-#: ../src/common/config.py:201 -#, fuzzy +#: ../src/common/config.py:205 msgid "Show tabbed notebook border in chat windows?" -msgstr "Mostrar borde pestañeado de bloc en las ventanas de conversación?" +msgstr "¿Mostrar borde solapado de archivador en las ventanas de conversación?"
-#: ../src/common/config.py:202 +#: ../src/common/config.py:206 msgid "Show close button in tab?" msgstr "¿Mostrar botón de cerrar en la pestaña?"
-#: ../src/common/config.py:203 +#: ../src/common/config.py:207 msgid "" "When negotiating an encrypted session, should Gajim assume you want your " "messages to be logged?" msgstr "" +"Cuando se negocie una sesión cifrada, ¿Gajim debería asumir que quieres que " +"tus mensajes sean registrados?"
-#: ../src/common/config.py:204 +#: ../src/common/config.py:208 msgid "" "A list of modp groups to use in a Diffie-Hellman, highest preference first, " "separated by commas. Valid groups are 1, 2, 5, 14, 15, 16, 17 and 18. Higher " "numbers are more secure, but take longer to calculate when you start a " "session." msgstr "" +"Una lista de grupos modp para usar en una lista de máxima preferencia Diffie-" +"Hellman, separados por commas. 1, 2, 5, 14, 15, 16, 17 y 18 son grupos " +"válidos. Los números más altos son los más seguros pero demoran más tiempo " +"en el cálculo cuando inicias una sesión."
-#: ../src/common/config.py:213 -#, fuzzy +#: ../src/common/config.py:217 msgid "Preview new messages in notification popup?" -msgstr "Noti_ficaciones del estado del chat" +msgstr "¿Previsualizar nuevos mensajes en un mensaje emergente?"
-#: ../src/common/config.py:218 +#: ../src/common/config.py:222 msgid "" "A semicolon-separated list of words that will be highlighted in group chats." msgstr "" "Una lista de palabras separadas por ';' que serán resaltadas en los grupos " "de charla."
-#: ../src/common/config.py:219 +#: ../src/common/config.py:223 msgid "" "If True, quits Gajim when X button of Window Manager is clicked. This " "setting is taken into account only if trayicon is used." @@ -3722,13 +3643,14 @@ "pulsado. Esta preferencia sólo se activa si se usa el icono del área de " "notificación. "
-#: ../src/common/config.py:220 -#, fuzzy +#: ../src/common/config.py:224 msgid "" "If True, Gajim will check if it's the default jabber client on each startup." -msgstr "Si es True, Gajim registra xmpp:// en cada inicio." +msgstr "" +"Si es True, Gajim comprueba si es el cliente Jabber por defecto en cada " +"inicio."
-#: ../src/common/config.py:221 +#: ../src/common/config.py:225 msgid "" "If True, Gajim will display an icon on each tab containing unread messages. " "Depending on the theme, this icon may be animated." @@ -3736,7 +3658,7 @@ "Si es True, Gajim mostrará un icono en cada pestaña que contenga mensajes " "sin leer. Dependiendo del tema, este icono puede ser animado."
-#: ../src/common/config.py:222 +#: ../src/common/config.py:226 msgid "" "If True, Gajim will display the status message, if not empty, for every " "contact under the contact name in roster window." @@ -3744,11 +3666,12 @@ "Si es True, Gajim mostrará el mensaje de estado, si no está vacío, para cada " "contacto bajo su nombre de contacto en ela ventana del roster."
-#: ../src/common/config.py:227 +#: ../src/common/config.py:231 msgid "Define the position of the avatar in roster. Can be left or right" msgstr "" +"Definir la posición del avatar en el roster. Puede ser izquierda o derecha"
-#: ../src/common/config.py:228 +#: ../src/common/config.py:232 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." @@ -3756,7 +3679,7 @@ "Si es True, Gajim pedirá un avatar a cada contacto que no tuvo un avatar la " "última vez o se tiene guardado uno demasiado antiguo."
-#: ../src/common/config.py:229 +#: ../src/common/config.py:233 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." @@ -3764,8 +3687,7 @@ "Si es False, Gajim nunca más mostrará la línea de estado cuando un contacto " "cambie su estado o su mensaje de estado"
-#: ../src/common/config.py:230 -#, fuzzy +#: ../src/common/config.py:234 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 " @@ -3779,30 +3701,30 @@ "mensajes de estado. Si es "in_and_out", Gajim sólo mostrará las entradas y " "salidas en el grupo de conversación."
-#: ../src/common/config.py:232 +#: ../src/common/config.py:236 msgid "Background color of contacts when they just signed in." msgstr "Color de fondo para los contactos que acaban de conectar."
-#: ../src/common/config.py:233 +#: ../src/common/config.py:237 msgid "Background color of contacts when they just signed out." msgstr "Color de fondo para los contactos que acaban de desconectar."
-#: ../src/common/config.py:235 +#: ../src/common/config.py:239 msgid "" "If True, restored messages will use a smaller font than the default one." msgstr "" "Si es True, los mensajes restaurados usarán una fuente más pequeña que la " "predefinida."
-#: ../src/common/config.py:236 +#: ../src/common/config.py:240 msgid "Don't show avatar for the transport itself." msgstr "No mostrar avatar para el transporte en sí mismo"
-#: ../src/common/config.py:237 +#: ../src/common/config.py:241 msgid "Don't show roster in the system taskbar." msgstr "No mostrar roster en la barra de tareas."
-#: ../src/common/config.py:238 +#: ../src/common/config.py:242 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 " @@ -3811,7 +3733,7 @@ "Si es True y las versiones instaladas de GTK+ y PyGTK son al menos 2.8, hace " "a la ventana resaltarse cuando se tienen eventos pendientes."
-#: ../src/common/config.py:240 +#: ../src/common/config.py:244 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 " @@ -3822,8 +3744,7 @@ "detener el envío de información sha en el grupo de conversación."
#. always, never, peracct, pertype should not be translated -#: ../src/common/config.py:243 -#, fuzzy +#: ../src/common/config.py:247 msgid "" "Controls the window where new messages are placed.\n" "'always' - All messages are sent to a single window.\n" @@ -3835,39 +3756,39 @@ "specific window." msgstr "" "Controla la ventana donde se depositan los nuevos mensajes.\n" -"'always' - Todos los mensajes son enviados a una ventana única.'never' - " -"Todos los mensajes obtienen su propia ventana.'peracct' - Los mensajes para " -"cada cuenta son enviados a una ventana específica.\n" -"'pertype' - Cada tipo de mensaje (p.ej., conversaciones vs. grupos de " -"charla) es enviado a una ventana específica. Nota: cambiar esta opción " -"requiere reiniciar Gajim para que los cambios tengan efecto." +"'always' - Todos los mensajes son enviados a una ventana única.\n" +"'always_with_roster' - Como 'always' pero en una ventana única a lo largo " +"del roster.\n" +"'never' - Todos los mensajes tienen su propia ventana\n" +"'peracct' - Los mensajes de cada cuenta se envían a una ventana específica.\n" +"'pertype' - Cada tipo de mensaje (p.ej. conversaciones vs. grupos de charla) " +"se envian a una ventana específica."
-#: ../src/common/config.py:244 +#: ../src/common/config.py:248 msgid "If False, you will no longer see the avatar in the chat window." msgstr "Si es False, no verás más el avatar en la ventana de conversación."
-#: ../src/common/config.py:245 +#: ../src/common/config.py:249 msgid "If True, pressing the escape key closes a tab/window." msgstr "Si es True, presionar la tecla escape cierra una ventana/pestaña."
-#: ../src/common/config.py:246 -#, fuzzy +#: ../src/common/config.py:250 msgid "Hides the buttons in chat windows." -msgstr "Oculta los botones en las ventanas de grupo de charla." +msgstr "Esconde los botones en las ventanas de conversación"
-#: ../src/common/config.py:247 +#: ../src/common/config.py:251 msgid "Hides the banner in a group chat window" msgstr "Oculta el banner en una ventana de grupo de charla"
-#: ../src/common/config.py:248 +#: ../src/common/config.py:252 msgid "Hides the banner in two persons chat window" msgstr "Oculta el banner en una ventana de conversación de dos personas."
-#: ../src/common/config.py:249 +#: ../src/common/config.py:253 msgid "Hides the group chat occupants list in group chat window." msgstr "Oculta los ocupantes en las ventans de grupo de charla."
-#: ../src/common/config.py:250 +#: ../src/common/config.py:254 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." @@ -3875,32 +3796,29 @@ "En una conversación, muestra el alias al principio de una línea sólo cuando " "no es la misma persona que envió el último mensaje."
-#: ../src/common/config.py:251 -#, fuzzy +#: ../src/common/config.py:255 msgid "Indentation when using merge consecutive nickname." msgstr "Indentar cuando se use una combinación de alias consecutivos."
-#: ../src/common/config.py:252 -#, fuzzy +#: ../src/common/config.py:256 msgid "Smooth scroll message in conversation window" -msgstr "Enviar mensaje y cerrar ventana" +msgstr "Desplazamiento suave del mensaje en las ventanas de conversación"
-#: ../src/common/config.py:253 -#, fuzzy +#: ../src/common/config.py:257 msgid "" "List of colors, separated by ":", that will be used to color nicknames in " "group chats." msgstr "" -"Lista de colores que serán usados para colorear alias en los grupos de " -"charla." +"Lista de colores, separados por ":", que serán usados para colorear alias " +"en los grupos de charla."
-#: ../src/common/config.py:254 +#: ../src/common/config.py:258 msgid "Ctrl-Tab go to next composing tab when none is unread." msgstr "" "Ctrl-Tab conduce a la próxima pestaña de composición cuando ninguno está sin " "leer."
-#: ../src/common/config.py:255 +#: ../src/common/config.py:259 msgid "" "Should we show the confirm metacontacts creation dialog or not? Empty string " "means we never show the dialog." @@ -3908,80 +3826,98 @@ "¿Debemos mostrar la confirmación de la creación de metacontactos o no? Una " "secuencia vacía significa que nunca se mostrará."
-#: ../src/common/config.py:256 -#, fuzzy +#: ../src/common/config.py:260 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 "" "Si es True, te será posible definir una prioridad negativa a tu cuenta en la " -"ventana de modificación de cuenta. SÉ CUIDADOSO, cuando estás conectado con " +"ventana de modificación de cuenta. TEN CUIDADO, cuando estás conectado con " "una prioridad negativa, NO recibirás ningún mensaje de tu servidor."
-#: ../src/common/config.py:257 +#: ../src/common/config.py:261 msgid "" "If True, Gajim will use Gnome Keyring (if available) to store account " "passwords." msgstr "" +"Si es True, Gajim usará Gnome Keyring (si está disponible) para almacenar " +"las contraseñas de la cuenta."
-#: ../src/common/config.py:258 +#: ../src/common/config.py:262 msgid "" "If True, Gajim will show number of online and total contacts in account and " "group rows." msgstr "" +"Si es True, Gajim mostrará el número de contactos totales y en línea en los " +"títulos de la cuenta y los grupos."
-#: ../src/common/config.py:259 +#: ../src/common/config.py:263 msgid "" "Can be empty, 'chat' or 'normal'. If not empty, treat all incoming messages " "as if they were of this type" msgstr "" +"Puede estar vacío, 'chat' o 'normal. Si no está vacío, tratará todos los " +"mensajes entrantes como si fueran de este tipo"
-#: ../src/common/config.py:260 +#: ../src/common/config.py:264 msgid "" "If True, Gajim will scroll and select the contact who sent you the last " "message, if chat window is not already opened." msgstr "" +"Si es True, Gajim mostrará y seleccionará el contacto que te envió el último " +"mensaje, si la ventana de conversación no ha sido abierta aún."
-#: ../src/common/config.py:261 +#: ../src/common/config.py:265 msgid "" "If True, Gajim will convert string between $$ and $$ to an image using dvips " "and convert before insterting it in chat window." msgstr "" +"Si es True, Gajim convertirá la cadena entre $$ y $$ a una imagen usando " +"dvips y convert antes de insertarla en una ventana de conversación."
-#: ../src/common/config.py:262 +#: ../src/common/config.py:266 msgid "Time of inactivity needed before the change status window closes down." -msgstr "" +msgstr "Tiempo de inactividad necesario antes de cerrar la ventana de estado."
-#: ../src/common/config.py:263 +#: ../src/common/config.py:267 msgid "" "Maximum number of lines that are printed in conversations. Oldest lines are " "cleared." msgstr "" +"Número máximo de líneas mostradas en las conversaciones. Las más antiguas " +"serán limpiadas."
-#: ../src/common/config.py:264 +#: ../src/common/config.py:268 msgid "" "If True, notification windows from notification-daemon will be attached to " "systray icon." msgstr "" +"Si es True, las ventanas de notificación del deminio notificador serán " +"adjuntadas al área de notificación."
-#: ../src/common/config.py:265 +#: ../src/common/config.py:269 msgid "Choose interval between 2 checks of idleness." -msgstr "" +msgstr "Elige intervalo entre 2 comprobaciones de inactividad."
-#: ../src/common/config.py:266 +#: ../src/common/config.py:270 msgid "" "Change the value to change the size of latex formulas displayed. The higher " "is larger." msgstr "" +"Cambiar el valor para cambiar el tamaño de las fórmulas latex visualizadas. " +"El mayor es el más grande."
-#: ../src/common/config.py:267 +#: ../src/common/config.py:271 +#, fuzzy msgid "" -"Valid uri schemes. Only schemes in this list will be made accepted as "real" -"" uri." +"Valid uri schemes. Only schemes in this list will be accepted as "real" " +"uri. (mailto and xmpp are handled seperately)" msgstr "" +"Combinaciones de uri válidas. Sólo las combinaciones de esta lista serán " +"aceptadas como uri "real"."
-#: ../src/common/config.py:278 +#: ../src/common/config.py:282 msgid "" "Priority will change automatically according to your status. Priorities are " "defined in autopriority_* options." @@ -3989,60 +3925,72 @@ "La prioridad cambiará automáticamente acorde a tu estado. Las prioridades " "están definidas en las opciones de autoprioridad_*"
-#: ../src/common/config.py:286 +#: ../src/common/config.py:290 msgid "" "Status used to autoconnect as. Can be online, chat, away, xa, dnd, " "invisible. NOTE: this option is used only if restore_last_status is disabled" msgstr "" +"Estado usado para las autoconexiones. Puede ser online, chat, away, xa, dnd, " +"invisible. NOTA: esta opción se usa sólo si restore_last_status está " +"desactivado"
-#: ../src/common/config.py:287 +#: ../src/common/config.py:291 msgid "If enabled, restore the last status that was used." -msgstr "" +msgstr "Si está activado, restaurar el último estado que fue usado"
-#: ../src/common/config.py:292 +#: ../src/common/config.py:296 msgid "" "If disabled, don't sign presences with GPG key, even if GPG is configured." msgstr "" +"Si está desactivado, no firmar presencias con la clave GPG, incluso si GPG " +"está configurado."
-#: ../src/common/config.py:294 +#: ../src/common/config.py:298 msgid "Enable ESessions encryption for this account." -msgstr "" +msgstr "Activar cifrado ESessions para esta cuenta."
-#: ../src/common/config.py:295 +#: ../src/common/config.py:299 msgid "Should Gajim automatically start an encrypted session when possible?" msgstr "" +"¿Gajim debería iniciar automáticamente una sesión cifrada cuando sea posible?"
-#: ../src/common/config.py:296 +#: ../src/common/config.py:300 msgid "" "Ordered list (space separated) of connection type to try. Can contain tls, " "ssl or plain" msgstr "" +"Lista ordenada (separada por espacios) de tipos de conexión para probar. " +"Puede contener tls, ssl o plain"
-#: ../src/common/config.py:297 +#: ../src/common/config.py:301 msgid "" "Show a warning dialog before sending password on an plaintext connection." msgstr "" +"Muestra un aviso antes de enviar la contraseña a través de una conexión de " +"texto plano."
-#: ../src/common/config.py:298 +#: ../src/common/config.py:302 msgid "Show a warning dialog before using standard SSL library." -msgstr "" +msgstr "Muestra un aviso antes de usar la librería SSL estándar"
-#: ../src/common/config.py:300 +#: ../src/common/config.py:304 msgid "Space separated list of ssl errors to ignore." -msgstr "" +msgstr "Lista separada por espacios de errores ssl a ignorar."
-#: ../src/common/config.py:312 +#: ../src/common/config.py:316 msgid "" "How many seconds to wait for the answer of keepalive packet before we try to " "reconnect." msgstr "" +"Cuántos segundos eseprar una respuesta de paquetes de mantenimiento de " +"actividad antes de intentar reconectar."
#. yes, no, ask -#: ../src/common/config.py:316 +#: ../src/common/config.py:320 msgid "Jabberd2 workaround" msgstr "Corrección Jabberd2"
-#: ../src/common/config.py:320 +#: ../src/common/config.py:324 msgid "" "If checked, Gajim will use your IP and proxies defined in " "file_transfer_proxies option for file transfer." @@ -4050,119 +3998,121 @@ "Si está marcado, Gajim usará tu IP y proxies definidos en la opción " "file_transfer_proxies para tu transferencia."
-#: ../src/common/config.py:334 +#: ../src/common/config.py:338 msgid "Answer to receipt requests" -msgstr "" +msgstr "Contestar peticiones recibidas"
-#: ../src/common/config.py:335 +#: ../src/common/config.py:339 msgid "Sent receipt requests" -msgstr "" +msgstr "Enviar peticiones recibidas"
-#: ../src/common/config.py:389 +#: ../src/common/config.py:402 msgid "Is OpenPGP enabled for this contact?" msgstr "¿Está OpenPGP activado para este contacto?"
-#: ../src/common/config.py:390 +#: ../src/common/config.py:403 msgid "" "Should Gajim automatically start an encrypted session with this contact when " "possible?" msgstr "" +"¿Debería Gajim iniciar automáticamente una sesión cifrada con este contacto " +"cuando sea posible?"
-#: ../src/common/config.py:391 ../src/common/config.py:394 +#: ../src/common/config.py:404 ../src/common/config.py:407 msgid "Language for which we want to check misspelled words" msgstr "Lenguaje en el que queremos comprobar errores ortográficos"
-#: ../src/common/config.py:400 +#: ../src/common/config.py:413 msgid "all or space separated status" msgstr "estado separado por espacios o all"
-#: ../src/common/config.py:401 +#: ../src/common/config.py:414 msgid "'yes', 'no', or 'both'" msgstr "'yes', 'no' o 'both'"
-#: ../src/common/config.py:402 ../src/common/config.py:404 -#: ../src/common/config.py:405 ../src/common/config.py:408 -#: ../src/common/config.py:409 +#: ../src/common/config.py:415 ../src/common/config.py:417 +#: ../src/common/config.py:418 ../src/common/config.py:421 +#: ../src/common/config.py:422 msgid "'yes', 'no' or ''" msgstr "'yes', 'no' o ''"
-#: ../src/common/config.py:415 ../src/common/pep.py:159 +#: ../src/common/config.py:428 ../src/common/pep.py:159 msgid "Sleeping" msgstr "Durmiendo"
-#: ../src/common/config.py:416 +#: ../src/common/config.py:429 msgid "Back soon" msgstr "Vuelvo pronto"
-#: ../src/common/config.py:416 +#: ../src/common/config.py:429 msgid "Back in some minutes." msgstr "Vuelvo en unos minutos."
-#: ../src/common/config.py:417 ../src/common/pep.py:129 +#: ../src/common/config.py:430 ../src/common/pep.py:129 msgid "Eating" msgstr "Comiendo"
-#: ../src/common/config.py:417 +#: ../src/common/config.py:430 msgid "I'm eating, so leave me a message." msgstr "Estoy comiendo, déjame un mensaje."
-#: ../src/common/config.py:418 +#: ../src/common/config.py:431 msgid "Movie" msgstr "Película"
-#: ../src/common/config.py:418 +#: ../src/common/config.py:431 msgid "I'm watching a movie." msgstr "Estoy viendo una película."
-#: ../src/common/config.py:419 ../src/common/pep.py:188 +#: ../src/common/config.py:432 ../src/common/pep.py:188 msgid "Working" msgstr "Trabajando"
-#: ../src/common/config.py:419 +#: ../src/common/config.py:432 msgid "I'm working." msgstr "Estoy trabajando."
-#: ../src/common/config.py:420 +#: ../src/common/config.py:433 msgid "Phone" msgstr "Teléfono"
-#: ../src/common/config.py:420 +#: ../src/common/config.py:433 msgid "I'm on the phone." msgstr "Estoy al teléfono."
-#: ../src/common/config.py:421 +#: ../src/common/config.py:434 msgid "Out" msgstr "Fuera"
-#: ../src/common/config.py:421 +#: ../src/common/config.py:434 msgid "I'm out enjoying life." msgstr "Estoy fuera disfrutando la vida"
-#: ../src/common/config.py:425 +#: ../src/common/config.py:438 msgid "I'm available." msgstr "Estoy disponible"
-#: ../src/common/config.py:426 +#: ../src/common/config.py:439 msgid "I'm free for chat." msgstr "Estoy libre para hablar"
-#: ../src/common/config.py:427 ../src/config.py:1397 +#: ../src/common/config.py:440 ../src/config.py:1451 msgid "Be right back." msgstr "Vuelvo pronto"
-#: ../src/common/config.py:428 +#: ../src/common/config.py:441 msgid "I'm not available." msgstr "No estoy disponible."
-#: ../src/common/config.py:429 +#: ../src/common/config.py:442 msgid "Do not disturb." msgstr "No molestar."
-#: ../src/common/config.py:430 ../src/common/config.py:431 +#: ../src/common/config.py:443 ../src/common/config.py:444 msgid "Bye!" msgstr "¡Adiós!"
-#: ../src/common/config.py:441 +#: ../src/common/config.py:454 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." @@ -4171,76 +4121,88 @@ "palabras de muc_highlight_words, o cuando un mensaje de grupo de charla " "contiene tu alias."
-#: ../src/common/config.py:442 +#: ../src/common/config.py:455 msgid "Sound to play when any MUC message arrives." msgstr "Sonido a reproducir cuando llega cualquier mensaje MUC"
-#: ../src/common/config.py:451 ../src/common/optparser.py:229 +#: ../src/common/config.py:464 ../src/common/optparser.py:231 msgid "green" msgstr "verde"
-#: ../src/common/config.py:455 ../src/common/optparser.py:215 +#: ../src/common/config.py:468 ../src/common/optparser.py:217 msgid "grocery" msgstr "grocery"
-#: ../src/common/config.py:459 +#: ../src/common/config.py:472 msgid "human" msgstr "human"
-#: ../src/common/config.py:463 +#: ../src/common/config.py:476 msgid "marine" msgstr "marino"
-#: ../src/common/connection_handlers.py:71 -#: ../src/common/zeroconf/connection_handlers_zeroconf.py:49 +#: ../src/common/connection_handlers.py:72 +#: ../src/common/zeroconf/connection_handlers_zeroconf.py:48 msgid "Unable to load idle module" msgstr "No se puede cargar el módulo idle"
-#: ../src/common/connection_handlers.py:233 -#: ../src/common/zeroconf/connection_handlers_zeroconf.py:93 +#: ../src/common/connection_handlers.py:234 +#: ../src/common/zeroconf/connection_handlers_zeroconf.py:90 msgid "Wrong host" msgstr "Host erróneo"
-#: ../src/common/connection_handlers.py:234 +#: ../src/common/connection_handlers.py:235 msgid "Invalid local address? :-O" -msgstr "" +msgstr "¿Dirección local no válida? :-O"
-#: ../src/common/connection_handlers.py:639 +#: ../src/common/connection_handlers.py:640 #, python-format msgid "Registration information for transport %s has not arrived in time" msgstr "La información de registro para el transporte %s no llegó a tiempo"
-#: ../src/common/connection_handlers.py:930 -#: ../src/common/connection_handlers.py:1857 -#: ../src/common/connection_handlers.py:1903 -#: ../src/common/connection_handlers.py:2091 -#: ../src/common/connection_handlers.py:2217 ../src/common/connection.py:1227 -#: ../src/gajim.py:392 +#: ../src/common/connection_handlers.py:927 +#: ../src/common/connection_handlers.py:1848 +#: ../src/common/connection_handlers.py:1894 +#: ../src/common/connection_handlers.py:2082 +#: ../src/common/connection_handlers.py:2215 ../src/common/connection.py:1258 +#: ../src/gajim.py:400 msgid "Disk Write Error" -msgstr "" +msgstr "Error de escritura en disco"
-#: ../src/common/connection_handlers.py:1137 ../src/common/connection.py:848 -#, fuzzy +#: ../src/common/connection_handlers.py:1134 ../src/common/connection.py:880 msgid "Invisibility not supported" -msgstr "Extensión no soportada" +msgstr "Invisibilidad no soportada"
-#: ../src/common/connection_handlers.py:1138 ../src/common/connection.py:849 +#: ../src/common/connection_handlers.py:1135 ../src/common/connection.py:881 #, python-format msgid "Account %s doesn't support invisibility." +msgstr "La cuenta %s no soporta invisibilidad." + +#: ../src/common/connection_handlers.py:1689 ../src/common/connection.py:1115 +#: ../src/config.py:1873 ../src/config.py:1881 ../src/config.py:1924 +#: ../src/config.py:3139 ../src/dataforms_widget.py:536 ../src/dialogs.py:2358 +msgid "Invalid Jabber ID" +msgstr "ID de Jabber no válida" + +#: ../src/common/connection_handlers.py:1690 +msgid "A message from a non-valid JID arrived, it has been ignored." msgstr ""
-#: ../src/common/connection_handlers.py:1860 -#: ../src/common/connection_handlers.py:1906 -#: ../src/common/connection_handlers.py:2094 -#: ../src/common/connection_handlers.py:2220 ../src/common/connection.py:1230 -#: ../src/gajim.py:171 +#: ../src/common/connection_handlers.py:1851 +#: ../src/common/connection_handlers.py:1897 +#: ../src/common/connection_handlers.py:2085 +#: ../src/common/connection_handlers.py:2218 ../src/common/connection.py:1261 +#: ../src/gajim.py:179 #, python-format msgid "" "The database file (%s) cannot be read. Try to repair it (see http://trac." "gajim.org/wiki/DatabaseBackup) or remove it (all history will be lost)." msgstr "" +"No se puede leer el archivo de base de datos (%s). Intenta repararlo (mira " +"http://trac.gajim.org/wiki/DatabaseBackup) o elimínalo (se perderá todo el " +"histórico)."
-#: ../src/common/connection_handlers.py:1978 +#: ../src/common/connection_handlers.py:1969 #, python-format msgid "Nickname not allowed: %s" msgstr "Alias no permitido: %s" @@ -4248,71 +4210,70 @@ #. we are banned #. group chat does not exist #. user may close the window before we are here +#: ../src/common/connection_handlers.py:2044 +#: ../src/common/connection_handlers.py:2047 +#: ../src/common/connection_handlers.py:2050 #: ../src/common/connection_handlers.py:2053 -#: ../src/common/connection_handlers.py:2056 -#: ../src/common/connection_handlers.py:2059 -#: ../src/common/connection_handlers.py:2062 -#: ../src/common/connection_handlers.py:2066 ../src/gajim.py:556 +#: ../src/common/connection_handlers.py:2057 ../src/gajim.py:563 msgid "Unable to join group chat" msgstr "No se pudo entrar al grupo de charla"
-#: ../src/common/connection_handlers.py:2054 -#, fuzzy, python-format +#: ../src/common/connection_handlers.py:2045 +#, python-format msgid "You are banned from group chat %s." -msgstr "Estás expulsado de este grupo de charla." +msgstr "Estás baneado del grupo de charla %s."
-#: ../src/common/connection_handlers.py:2057 -#, fuzzy, python-format +#: ../src/common/connection_handlers.py:2048 +#, python-format msgid "Group chat %s does not exist." -msgstr "No existe el grupo de charla." +msgstr "El grupo de charla %s no existe."
-#: ../src/common/connection_handlers.py:2060 +#: ../src/common/connection_handlers.py:2051 msgid "Group chat creation is restricted." msgstr "La creación de grupos de charla está restringida."
-#: ../src/common/connection_handlers.py:2063 -#, fuzzy, python-format +#: ../src/common/connection_handlers.py:2054 +#, python-format msgid "Your registered nickname must be used in group chat %s." -msgstr "Es necesario usar el alias registrado." +msgstr "Tu alias registrado podría estar en uso en el grupo de charla %s."
-#: ../src/common/connection_handlers.py:2067 -#, fuzzy, python-format +#: ../src/common/connection_handlers.py:2058 +#, python-format msgid "You are not in the members list in groupchat %s." -msgstr "No estás en la lista de miembros" +msgstr "No estás en la lista de miembros del grupo de charla %s."
#. Room has been destroyed. see #. http://www.xmpp.org/extensions/xep-0045.html#destroyroom -#: ../src/common/connection_handlers.py:2109 -#, fuzzy +#: ../src/common/connection_handlers.py:2100 msgid "Room has been destroyed" -msgstr "La autorización ha sido eliminada" +msgstr "El salón ha sido destruido"
-#: ../src/common/connection_handlers.py:2116 +#: ../src/common/connection_handlers.py:2107 #, python-format msgid "You can join this room instead: %s" -msgstr "" +msgstr "Puedes unirte a este salón en su lugar: %s"
-#: ../src/common/connection_handlers.py:2143 +#: ../src/common/connection_handlers.py:2134 msgid "I would like to add you to my roster." msgstr "Me gustaría añadirte a mi lista de contactos"
#. BE CAREFUL: no con.updateRosterItem() in a callback -#: ../src/common/connection_handlers.py:2164 +#: ../src/common/connection_handlers.py:2155 #, python-format msgid "we are now subscribed to %s" msgstr "estamos ahora suscritos a %s"
-#: ../src/common/connection_handlers.py:2166 +#: ../src/common/connection_handlers.py:2157 #, python-format msgid "unsubscribe request from %s" msgstr "petición de eliminación de suscripción de %s"
-#: ../src/common/connection_handlers.py:2168 +#: ../src/common/connection_handlers.py:2159 #, python-format msgid "we are now unsubscribed from %s" msgstr "ya no estamos suscritos a %s"
-#: ../src/common/connection_handlers.py:2343 +#: ../src/common/connection_handlers.py:2341 #, python-format msgid "" "JID %s is not RFC compliant. It will not be added to your roster. Use roster " @@ -4323,305 +4284,313 @@ "eliminarlo"
#. We didn't set a passphrase -#: ../src/common/connection_handlers.py:2368 -#: ../src/common/zeroconf/connection_zeroconf.py:172 +#: ../src/common/connection_handlers.py:2366 +#: ../src/common/zeroconf/connection_zeroconf.py:173 msgid "OpenPGP passphrase was not given" msgstr "No se ha facilitado la clave OpenPGP"
#. %s is the account name here -#: ../src/common/connection_handlers.py:2370 -#: ../src/common/zeroconf/connection_zeroconf.py:174 -#: ../src/roster_window.py:1931 ../src/roster_window.py:1951 +#: ../src/common/connection_handlers.py:2368 +#: ../src/common/zeroconf/connection_zeroconf.py:175 +#: ../src/roster_window.py:1920 ../src/roster_window.py:1940 #, python-format msgid "You will be connected to %s without OpenPGP." msgstr "Te conectarás a %s sin OpenPGP"
-#: ../src/common/connection.py:66 +#: ../src/common/connection.py:67 msgid "Unable to get issuer certificate" -msgstr "" +msgstr "No se puede obtener el certificado del emisor"
-#: ../src/common/connection.py:67 +#: ../src/common/connection.py:68 msgid "Unable to get certificate CRL" -msgstr "" +msgstr "No se puede obtener el certificado CRL"
-#: ../src/common/connection.py:68 +#: ../src/common/connection.py:69 msgid "Unable to decrypt certificate's signature" -msgstr "" +msgstr "No se puede descifrar la firma del certificado"
-#: ../src/common/connection.py:69 +#: ../src/common/connection.py:70 msgid "Unable to decrypt CRL's signature" -msgstr "" +msgstr "No se puede descifrar la firma del CRL"
-#: ../src/common/connection.py:70 -#, fuzzy +#: ../src/common/connection.py:71 msgid "Unable to decode issuer public key" -msgstr "No se puede cargar el módulo idle" +msgstr "No se puede decodificar la clave pública del emisor"
-#: ../src/common/connection.py:71 +#: ../src/common/connection.py:72 msgid "Certificate signature failure" -msgstr "" +msgstr "Fallo en la firma del certificado"
-#: ../src/common/connection.py:72 +#: ../src/common/connection.py:73 msgid "CRL signature failure" -msgstr "" +msgstr "Fallo en la firma CRL"
-#: ../src/common/connection.py:73 +#: ../src/common/connection.py:74 msgid "Certificate is not yet valid" -msgstr "" +msgstr "El certificado todavía no es válido"
-#: ../src/common/connection.py:74 +#: ../src/common/connection.py:75 msgid "Certificate has expired" -msgstr "" +msgstr "El certificado ha expirado"
-#: ../src/common/connection.py:75 +#: ../src/common/connection.py:76 msgid "CRL is not yet valid" -msgstr "" +msgstr "El CRL todavía no es válido"
-#: ../src/common/connection.py:76 +#: ../src/common/connection.py:77 msgid "CRL has expired" -msgstr "" +msgstr "El CRL ha expirado"
-#: ../src/common/connection.py:77 +#: ../src/common/connection.py:78 msgid "Format error in certificate's notBefore field" -msgstr "" +msgstr "Error de formato en el campo notBefore del certificado"
-#: ../src/common/connection.py:78 +#: ../src/common/connection.py:79 msgid "Format error in certificate's notAfter field" -msgstr "" +msgstr "Error de formato en el campo notAfter del certificado"
-#: ../src/common/connection.py:79 +#: ../src/common/connection.py:80 msgid "Format error in CRL's lastUpdate field" -msgstr "" +msgstr "Error de formato en el campo lastUpdate del CRL"
-#: ../src/common/connection.py:80 +#: ../src/common/connection.py:81 msgid "Format error in CRL's nextUpdate field" -msgstr "" +msgstr "Error de formato en el campo nextUpdate del CRL"
-#: ../src/common/connection.py:81 +#: ../src/common/connection.py:82 msgid "Out of memory" -msgstr "" +msgstr "Memoria llena"
-#: ../src/common/connection.py:82 +#: ../src/common/connection.py:83 msgid "Self signed certificate" -msgstr "" +msgstr "Certificado autofirmado"
-#: ../src/common/connection.py:83 +#: ../src/common/connection.py:84 msgid "Self signed certificate in certificate chain" -msgstr "" +msgstr "Certificado autofirmado en la cadena del certificado"
-#: ../src/common/connection.py:84 +#: ../src/common/connection.py:85 msgid "Unable to get local issuer certificate" -msgstr "" +msgstr "No se puede obtener el certificado del emisor local"
-#: ../src/common/connection.py:85 +#: ../src/common/connection.py:86 msgid "Unable to verify the first certificate" -msgstr "" +msgstr "No se puede verificar el primer certificado"
-#: ../src/common/connection.py:86 +#: ../src/common/connection.py:87 msgid "Certificate chain too long" -msgstr "" +msgstr "La cadena del certificado es demasiado larga"
-#: ../src/common/connection.py:87 +#: ../src/common/connection.py:88 msgid "Certificate revoked" -msgstr "" +msgstr "Certificado revocado"
-#: ../src/common/connection.py:88 -#, fuzzy +#: ../src/common/connection.py:89 msgid "Invalid CA certificate" -msgstr "Nombre de usuario no válido" +msgstr "Certificado CA no válido"
-#: ../src/common/connection.py:89 +#: ../src/common/connection.py:90 msgid "Path length constraint exceeded" -msgstr "" +msgstr "La longitud de la ruta excedió el límite"
-#: ../src/common/connection.py:90 +#: ../src/common/connection.py:91 msgid "Unsupported certificate purpose" -msgstr "" +msgstr "Propósito de certificado no soportado"
-#: ../src/common/connection.py:91 +#: ../src/common/connection.py:92 msgid "Certificate not trusted" -msgstr "" +msgstr "El certificado no es de confianza"
-#: ../src/common/connection.py:92 +#: ../src/common/connection.py:93 msgid "Certificate rejected" -msgstr "" +msgstr "Certificado rechazado"
-#: ../src/common/connection.py:93 +#: ../src/common/connection.py:94 msgid "Subject issuer mismatch" -msgstr "" +msgstr "El emisor del asunto no coincide"
-#: ../src/common/connection.py:94 +#: ../src/common/connection.py:95 msgid "Authority and subject key identifier mismatch" -msgstr "" +msgstr "La autoridad y el asunto de la clave identificadora no coinciden"
-#: ../src/common/connection.py:95 +#: ../src/common/connection.py:96 msgid "Authority and issuer serial number mismatch" -msgstr "" +msgstr "La autoridad y el número de serie del emisor no coinciden"
-#: ../src/common/connection.py:96 +#: ../src/common/connection.py:97 msgid "Key usage does not include certificate signing" -msgstr "" +msgstr "El uso de la clave no incluye la firma de certificados"
-#: ../src/common/connection.py:97 +#: ../src/common/connection.py:98 msgid "Application verification failure" -msgstr "" +msgstr "Falló la verificación de la aplicación"
-#: ../src/common/connection.py:266 -#: ../src/common/zeroconf/connection_zeroconf.py:214 +#: ../src/common/connection.py:267 +#: ../src/common/zeroconf/connection_zeroconf.py:215 #, python-format msgid "Connection with account "%s" has been lost" msgstr "Se ha perdido la conexión de la cuenta "%s""
-#: ../src/common/connection.py:267 +#: ../src/common/connection.py:268 msgid "Reconnect manually." msgstr "Reconectar manualmente."
-#: ../src/common/connection.py:278 -#, fuzzy, python-format +#: ../src/common/connection.py:279 +#, python-format msgid "Server %(name)s answered wrongly to register request: %(error)s" msgstr "" -"El transporte %s respondió incorrectamente a la petición de registro: %s" +"El servidor %(name)s contestó incorrectamente a la petición de registro: %" +"(error)s"
-#: ../src/common/connection.py:312 +#: ../src/common/connection.py:313 #, python-format msgid "Server %s provided a different registration form" -msgstr "" +msgstr "El servidor %s provee un formulario de registro diferente"
-#: ../src/common/connection.py:325 -#, fuzzy, python-format +#: ../src/common/connection.py:326 +#, python-format msgid "Unknown SSL error: %d" -msgstr "Versión de D-Bus desconocida: %s" +msgstr "Error SSL desconocido: %d"
#. wrong answer -#: ../src/common/connection.py:340 +#: ../src/common/connection.py:341 msgid "Invalid answer" msgstr "Respuesta no válida"
-#: ../src/common/connection.py:341 -#, fuzzy, python-format +#: ../src/common/connection.py:342 +#, python-format msgid "Transport %(name)s answered wrongly to register request: %(error)s" msgstr "" -"El transporte %s respondió incorrectamente a la petición de registro: %s" +"El transporte %(name)s respondió incorrectamente a la petición de registro: %" +"(error)s"
-#: ../src/common/connection.py:522 -#, fuzzy -msgid "Connection to proxy failed" -msgstr "Falló la conexión" - -#: ../src/common/connection.py:610 ../src/common/connection.py:711 -#: ../src/common/connection.py:1362 -#: ../src/common/zeroconf/connection_zeroconf.py:248 +#: ../src/common/connection.py:629 ../src/common/connection.py:743 +#: ../src/common/connection.py:1393 +#: ../src/common/zeroconf/connection_zeroconf.py:249 #, python-format msgid "Could not connect to "%s"" msgstr "No se puede conectar a "%s""
-#: ../src/common/connection.py:611 ../src/gajim.py:1052 +#: ../src/common/connection.py:630 ../src/gajim.py:1057 msgid "Check your connection or try again later." msgstr "Comprueba tu conexión o reinténtalo más tarde."
-#: ../src/common/connection.py:643 ../src/common/connection.py:691 -#, fuzzy, python-format +#: ../src/common/connection.py:639 +msgid "Connection to proxy failed" +msgstr "Falló la conexión al proxy" + +#: ../src/common/connection.py:670 ../src/common/connection.py:723 +#, python-format msgid "Could not connect to account %s" -msgstr "No se puede conectar a "%s"" +msgstr "No se puede conectar a la cuenta %s"
-#: ../src/common/connection.py:644 ../src/common/connection.py:692 -#, fuzzy, python-format +#: ../src/common/connection.py:671 ../src/common/connection.py:724 +#, python-format msgid "Connection with account %s has been lost. Retry connecting." -msgstr "Se ha perdido la conexión de la cuenta "%s"" +msgstr "Se perdió la conexión con la cuenta %s. Reintentando la conexión."
-#: ../src/common/connection.py:666 +#: ../src/common/connection.py:693 #, python-format msgid "The authenticity of the %s certificate could be invalid." -msgstr "" +msgstr "La autenticidad del certificado %s podría no ser válida."
-#: ../src/common/connection.py:669 +#: ../src/common/connection.py:696 #, python-format msgid "" "\n" "SSL Error: <b>%s</b>" msgstr "" +"\n" +"Error SSL: <b>%s</b>"
-#: ../src/common/connection.py:671 -#, fuzzy, python-format +#: ../src/common/connection.py:698 +#, python-format msgid "" "\n" "Unknown SSL error: %d" -msgstr "Versión de D-Bus desconocida: %s" +msgstr "" +"\n" +"Error SSL desconocido: %d"
-#: ../src/common/connection.py:712 +#: ../src/common/connection.py:744 msgid "Check your connection or try again later" msgstr "Comprueba tu conexión o inténtalo más tarde"
-#: ../src/common/connection.py:737 +#: ../src/common/connection.py:769 #, python-format msgid "Authentication failed with "%s"" msgstr "Falló la autentificación con "%s""
-#: ../src/common/connection.py:739 +#: ../src/common/connection.py:771 msgid "Please check your login and password for correctness." msgstr "" "Por favor, comprueba que tu nombre de usuario y contraseña sean correctos."
-#: ../src/common/connection.py:800 -#, fuzzy +#: ../src/common/connection.py:832 msgid "Error while removing privacy list" -msgstr "Error añadiendo el servicio. %s" +msgstr "Error eliminando la lista de privacidad"
-#: ../src/common/connection.py:801 +#: ../src/common/connection.py:833 #, 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 "" +"La lista de privacidad %s no se ha eliminado. Podría estar activa en uno de " +"tus recursos conectados. Desactívalo y prueba de nuevo."
-#: ../src/common/connection.py:1094 -#: ../src/common/zeroconf/connection_zeroconf.py:381 +#: ../src/common/connection.py:1116 ../src/dialogs.py:2359 +#, fuzzy, python-format +msgid "It is not possible to send a message to %s, this JID is not valid." +msgstr "No es posible enviar archivos vacíos" + +#: ../src/common/connection.py:1137 +#: ../src/common/zeroconf/connection_zeroconf.py:389 msgid "Neither the remote presence is signed, nor a key was assigned." -msgstr "" +msgstr "Ni la presencia remota ha sido firmada, ni se ha asignado una clave"
-#: ../src/common/connection.py:1096 -#: ../src/common/zeroconf/connection_zeroconf.py:383 +#: ../src/common/connection.py:1139 +#: ../src/common/zeroconf/connection_zeroconf.py:391 #, python-format msgid "The contact's key (%s) does not match the key assigned in Gajim." -msgstr "" +msgstr "La clave del contacto (%s) no coincide con la clave asignada en Gajim."
#. we're not english #. one in locale and one en -#: ../src/common/connection.py:1105 -#, fuzzy +#: ../src/common/connection.py:1148 msgid "[This message is *encrypted* (See :XEP:`27`]" -msgstr "[Este mensaje está *encriptado* (Mira :JEP:`27`]" +msgstr "[Este mensaje está *cifrado* (Mira :JEP:`27`]"
-#: ../src/common/connection.py:1217 -#: ../src/common/zeroconf/connection_zeroconf.py:445 -#, fuzzy, python-format +#: ../src/common/connection.py:1248 +#: ../src/common/zeroconf/connection_zeroconf.py:468 +#, python-format msgid "" "Subject: %(subject)s\n" "%(message)s" msgstr "" -"Tema: %s\n" -"%s" +"Tema: %(subject)s\n" +"%(message)s"
-#: ../src/common/connection.py:1395 +#: ../src/common/connection.py:1426 msgid "Not fetched because of invisible status" msgstr "No conseguido debido al estado invisible"
-#: ../src/common/contacts.py:92 ../src/common/helpers.py:73 +#: ../src/common/contacts.py:92 ../src/common/helpers.py:72 +#: ../src/gajim.py:980 msgid "Observers" msgstr "Observadores"
#: ../src/common/contacts.py:96 ../src/common/contacts.py:348 -#: ../src/common/helpers.py:73 ../src/disco.py:117 ../src/disco.py:118 -#: ../src/disco.py:1348 ../src/gajim.py:802 ../src/roster_window.py:818 -#: ../src/roster_window.py:1471 ../src/roster_window.py:1538 -#: ../src/roster_window.py:1540 ../src/roster_window.py:1677 +#: ../src/common/helpers.py:72 ../src/disco.py:117 ../src/disco.py:118 +#: ../src/disco.py:1349 ../src/gajim.py:809 ../src/roster_window.py:815 +#: ../src/roster_window.py:1466 ../src/roster_window.py:1533 +#: ../src/roster_window.py:1535 ../src/roster_window.py:1672 msgid "Transports" msgstr "Transportes"
#: ../src/common/contacts.py:356 -#, fuzzy msgid "Not in roster" -msgstr "no está en el roster" +msgstr "No está en el roster"
#. only say that to non Windows users #: ../src/common/dbus_support.py:47 @@ -4637,9 +4606,8 @@ msgstr "falta la dependencia pysqlite2 (tcc python-pysqlite2). Saliendo..."
#: ../src/common/exceptions.py:47 -#, fuzzy msgid "Database cannot be read." -msgstr "El nombre de la cuenta no puede estar vacío." +msgstr "No se pudo leer la base de datos."
#: ../src/common/exceptions.py:55 msgid "Service not available: Gajim is not running, or remote_control is False" @@ -4710,69 +4678,56 @@ #. 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:47 ../src/common/fuzzyclock.py:55 -#, fuzzy msgid "$0 o'clock" -msgstr "%0 en punto" +msgstr "$0 en punto"
#: ../src/common/fuzzyclock.py:47 ../src/common/fuzzyclock.py:55 -#, fuzzy msgid "five past $0" -msgstr "%0 y cinco" +msgstr "$0 y cinco"
#: ../src/common/fuzzyclock.py:48 ../src/common/fuzzyclock.py:56 -#, fuzzy msgid "ten past $0" -msgstr "%0 y diez" +msgstr "$0 y diez"
#: ../src/common/fuzzyclock.py:48 ../src/common/fuzzyclock.py:56 -#, fuzzy msgid "quarter past $0" -msgstr "%0 y cuarto" +msgstr "$0 y cuarto"
#: ../src/common/fuzzyclock.py:49 ../src/common/fuzzyclock.py:57 -#, fuzzy msgid "twenty past $0" -msgstr "%0 y veinte" +msgstr "$0 y veinte"
#: ../src/common/fuzzyclock.py:49 ../src/common/fuzzyclock.py:57 -#, fuzzy msgid "twenty five past $0" -msgstr "%0 y veinticinco" +msgstr "$0 y veinticinco"
#: ../src/common/fuzzyclock.py:50 ../src/common/fuzzyclock.py:58 -#, fuzzy msgid "half past $0" -msgstr "%0 y media" +msgstr "$0 y media"
#: ../src/common/fuzzyclock.py:50 ../src/common/fuzzyclock.py:58 -#, fuzzy msgid "twenty five to $1" -msgstr "%1 menos veinticinco" +msgstr "$1 menos veinticinco"
#: ../src/common/fuzzyclock.py:51 ../src/common/fuzzyclock.py:59 -#, fuzzy msgid "twenty to $1" -msgstr "%1 menos veinte" +msgstr "$1 menos veinte"
#: ../src/common/fuzzyclock.py:51 ../src/common/fuzzyclock.py:59 -#, fuzzy msgid "quarter to $1" -msgstr "%1 menos cuarto" +msgstr "$1 menos cuarto"
#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60 -#, fuzzy msgid "ten to $1" -msgstr "%1 menos diez" +msgstr "$1 menos diez"
#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:60 -#, fuzzy msgid "five to $1" -msgstr "%1 menos cinco" +msgstr "$1 menos cinco"
#: ../src/common/fuzzyclock.py:52 ../src/common/fuzzyclock.py:61 -#, fuzzy msgid "$1 o'clock" -msgstr "%1 en punto" +msgstr "$1 en punto"
#: ../src/common/fuzzyclock.py:64 msgid "Night" @@ -4822,235 +4777,235 @@ msgid "Weekend!" msgstr "¡Fin de semana!"
-#: ../src/common/helpers.py:158 +#: ../src/common/helpers.py:157 msgid "Invalid character in username." msgstr "Caracter no válido en el nombre de usuario."
-#: ../src/common/helpers.py:163 +#: ../src/common/helpers.py:162 msgid "Server address required." msgstr "Se requiere la dirección del servidor."
-#: ../src/common/helpers.py:169 +#: ../src/common/helpers.py:168 msgid "Invalid character in hostname." msgstr "Carácter no válido en el nombre del host."
-#: ../src/common/helpers.py:176 +#: ../src/common/helpers.py:175 msgid "Invalid character in resource." msgstr "Carácter no válido en el recurso."
#. GiB means gibibyte -#: ../src/common/helpers.py:216 +#: ../src/common/helpers.py:215 #, python-format msgid "%s GiB" msgstr ""
#. GB means gigabyte -#: ../src/common/helpers.py:219 +#: ../src/common/helpers.py:218 #, python-format msgid "%s GB" msgstr ""
#. MiB means mibibyte -#: ../src/common/helpers.py:223 +#: ../src/common/helpers.py:222 #, python-format msgid "%s MiB" msgstr ""
#. MB means megabyte -#: ../src/common/helpers.py:226 +#: ../src/common/helpers.py:225 #, python-format msgid "%s MB" msgstr ""
#. KiB means kibibyte -#: ../src/common/helpers.py:230 +#: ../src/common/helpers.py:229 #, python-format msgid "%s KiB" msgstr ""
#. KB means kilo bytes -#: ../src/common/helpers.py:233 +#: ../src/common/helpers.py:232 #, python-format msgid "%s KB" msgstr ""
#. B means bytes -#: ../src/common/helpers.py:236 +#: ../src/common/helpers.py:235 #, python-format msgid "%s B" msgstr ""
-#: ../src/common/helpers.py:267 +#: ../src/common/helpers.py:266 msgid "_Busy" msgstr "_Ocupado"
-#: ../src/common/helpers.py:269 +#: ../src/common/helpers.py:268 msgid "Busy" msgstr "Ocupado"
-#: ../src/common/helpers.py:272 +#: ../src/common/helpers.py:271 msgid "_Not Available" msgstr "_No Disponible"
-#: ../src/common/helpers.py:277 +#: ../src/common/helpers.py:276 msgid "_Free for Chat" msgstr "_Libre para hablar"
-#: ../src/common/helpers.py:279 +#: ../src/common/helpers.py:278 msgid "Free for Chat" msgstr "Libre para hablar"
-#: ../src/common/helpers.py:282 +#: ../src/common/helpers.py:281 msgid "_Available" msgstr "En líne_a"
-#: ../src/common/helpers.py:284 ../src/features_window.py:121 +#: ../src/common/helpers.py:283 ../src/features_window.py:119 msgid "Available" msgstr "En línea"
-#: ../src/common/helpers.py:286 +#: ../src/common/helpers.py:285 msgid "Connecting" msgstr "Conectando"
-#: ../src/common/helpers.py:289 +#: ../src/common/helpers.py:288 msgid "A_way" msgstr "A_usente"
-#: ../src/common/helpers.py:294 +#: ../src/common/helpers.py:293 msgid "_Offline" msgstr "D_esconectado"
-#: ../src/common/helpers.py:296 +#: ../src/common/helpers.py:295 msgid "Offline" msgstr "Desconectado"
-#: ../src/common/helpers.py:299 +#: ../src/common/helpers.py:298 msgid "_Invisible" msgstr "_Invisible"
-#: ../src/common/helpers.py:305 +#: ../src/common/helpers.py:304 msgid "?contact has status:Unknown" msgstr "Desconocido"
-#: ../src/common/helpers.py:307 +#: ../src/common/helpers.py:306 msgid "?contact has status:Has errors" msgstr "Tiene errores"
-#: ../src/common/helpers.py:312 +#: ../src/common/helpers.py:311 msgid "?Subscription we already have:None" msgstr "Ninguna"
-#: ../src/common/helpers.py:314 +#: ../src/common/helpers.py:313 msgid "To" msgstr "A"
-#: ../src/common/helpers.py:318 +#: ../src/common/helpers.py:317 msgid "Both" msgstr "Ambos"
-#: ../src/common/helpers.py:326 +#: ../src/common/helpers.py:325 msgid "?Ask (for Subscription):None" msgstr "Ninguna"
-#: ../src/common/helpers.py:328 +#: ../src/common/helpers.py:327 msgid "Subscribe" msgstr "_Añadir"
-#: ../src/common/helpers.py:337 +#: ../src/common/helpers.py:336 msgid "?Group Chat Contact Role:None" msgstr "Ninguno"
-#: ../src/common/helpers.py:340 +#: ../src/common/helpers.py:339 msgid "Moderators" msgstr "Moderadores"
-#: ../src/common/helpers.py:342 +#: ../src/common/helpers.py:341 msgid "Moderator" msgstr "Moderador"
-#: ../src/common/helpers.py:345 +#: ../src/common/helpers.py:344 msgid "Participants" msgstr "Participantes"
-#: ../src/common/helpers.py:347 +#: ../src/common/helpers.py:346 msgid "Participant" msgstr "Participante"
-#: ../src/common/helpers.py:350 +#: ../src/common/helpers.py:349 msgid "Visitors" msgstr "Visitantes"
-#: ../src/common/helpers.py:352 +#: ../src/common/helpers.py:351 msgid "Visitor" msgstr "Visitante"
-#: ../src/common/helpers.py:358 +#: ../src/common/helpers.py:357 msgid "?Group Chat Contact Affiliation:None" msgstr "Ninguno"
-#: ../src/common/helpers.py:360 +#: ../src/common/helpers.py:359 msgid "Owner" msgstr "Propietario"
-#: ../src/common/helpers.py:362 +#: ../src/common/helpers.py:361 msgid "Administrator" msgstr "Administrador"
-#: ../src/common/helpers.py:364 +#: ../src/common/helpers.py:363 msgid "Member" msgstr "Miembro"
-#: ../src/common/helpers.py:401 +#: ../src/common/helpers.py:400 msgid "is paying attention to the conversation" msgstr "está prestando atención a la conversación"
-#: ../src/common/helpers.py:403 +#: ../src/common/helpers.py:402 msgid "is doing something else" msgstr "está haciendo algo más"
-#: ../src/common/helpers.py:405 +#: ../src/common/helpers.py:404 msgid "is composing a message..." msgstr "está escribiendo..."
#. paused means he or she was composing but has stopped for a while -#: ../src/common/helpers.py:408 +#: ../src/common/helpers.py:407 msgid "paused composing a message" msgstr "ha parado de escribir"
-#: ../src/common/helpers.py:410 +#: ../src/common/helpers.py:409 msgid "has closed the chat window or tab" msgstr "ha cerrado la ventana de chat"
-#: ../src/common/helpers.py:992 ../src/common/helpers.py:999 -#, fuzzy, python-format +#: ../src/common/helpers.py:983 ../src/common/helpers.py:990 +#, python-format msgid "%d message pending" msgid_plural "%d messages pending" -msgstr[0] "Enviar mensaje" -msgstr[1] "Enviar mensaje" +msgstr[0] "%d mensaje pendiente" +msgstr[1] "%d mensajes pendientes"
-#: ../src/common/helpers.py:1005 -#, fuzzy, python-format +#: ../src/common/helpers.py:996 +#, python-format msgid " from room %s" -msgstr "De %s" +msgstr " del salón %s"
-#: ../src/common/helpers.py:1008 ../src/common/helpers.py:1027 -#, fuzzy, python-format +#: ../src/common/helpers.py:999 ../src/common/helpers.py:1018 +#, python-format msgid " from user %s" -msgstr "De %s" +msgstr " del usuario %s"
-#: ../src/common/helpers.py:1010 -#, fuzzy, python-format +#: ../src/common/helpers.py:1001 +#, python-format msgid " from %s" -msgstr "De %s" +msgstr " de %s"
-#: ../src/common/helpers.py:1017 ../src/common/helpers.py:1024 +#: ../src/common/helpers.py:1008 ../src/common/helpers.py:1015 #, python-format msgid "%d event pending" msgid_plural "%d events pending" msgstr[0] "" msgstr[1] ""
-#: ../src/common/helpers.py:1057 +#: ../src/common/helpers.py:1048 #, python-format msgid "Gajim - %s" msgstr "Gajim - %s" @@ -5061,14 +5016,13 @@ msgid "error: cannot open %s for reading" msgstr "error: no se puede abrir %s para lectura"
-#: ../src/common/optparser.py:224 ../src/common/optparser.py:225 +#: ../src/common/optparser.py:226 ../src/common/optparser.py:227 msgid "cyan" -msgstr "" +msgstr "cyan"
-#: ../src/common/optparser.py:341 -#, fuzzy +#: ../src/common/optparser.py:343 msgid "migrating logs database to indices" -msgstr "migrando registros de la base de datos a los índices" +msgstr "migrando la base de datos de registros a los índices"
#: ../src/common/passwords.py:92 #, python-format @@ -5077,710 +5031,639 @@
#: ../src/common/pep.py:29 msgid "Afraid" -msgstr "" +msgstr "Miedoso"
#: ../src/common/pep.py:30 msgid "Amazed" -msgstr "" +msgstr "Impresionado"
#: ../src/common/pep.py:31 msgid "Amorous" -msgstr "" +msgstr "Amoroso"
#: ../src/common/pep.py:32 msgid "Angry" -msgstr "" +msgstr "Enfadado"
#: ../src/common/pep.py:33 msgid "Annoyed" -msgstr "" +msgstr "Molesto"
#: ../src/common/pep.py:34 msgid "Anxious" -msgstr "" +msgstr "Ansioso"
#: ../src/common/pep.py:35 -#, fuzzy msgid "Aroused" -msgstr "Pausado" +msgstr "Despierto"
#: ../src/common/pep.py:36 msgid "Ashamed" -msgstr "" +msgstr "Avergonzado"
#: ../src/common/pep.py:37 -#, fuzzy msgid "Bored" -msgstr "Negrita" +msgstr "Aburrido"
#: ../src/common/pep.py:38 -#, fuzzy msgid "Brave" -msgstr "Tiene " +msgstr "Animado"
#: ../src/common/pep.py:39 msgid "Calm" -msgstr "" +msgstr "Calmado"
#: ../src/common/pep.py:40 -#, fuzzy msgid "Cautious" -msgstr "Charlas" +msgstr "Cauto"
#: ../src/common/pep.py:41 -#, fuzzy msgid "Cold" -msgstr "Negrita" +msgstr "Frío"
#: ../src/common/pep.py:42 -#, fuzzy msgid "Confident" -msgstr "_Contenidos" +msgstr "Confiado"
#: ../src/common/pep.py:43 msgid "Confused" -msgstr "" +msgstr "Confuso"
#: ../src/common/pep.py:44 -#, fuzzy msgid "Contemplative" -msgstr "Completado" +msgstr "Contemplativo"
#: ../src/common/pep.py:45 -#, fuzzy msgid "Contented" -msgstr "_Contenidos" +msgstr "Contento"
#: ../src/common/pep.py:46 msgid "Cranky" -msgstr "" +msgstr "Irritable"
#: ../src/common/pep.py:47 msgid "Crazy" -msgstr "" +msgstr "Alocado"
#: ../src/common/pep.py:48 -#, fuzzy msgid "Creative" -msgstr "Inactivo" +msgstr "Creativo"
#: ../src/common/pep.py:49 -#, fuzzy msgid "Curious" -msgstr "_Anterior" +msgstr "Curioso"
#: ../src/common/pep.py:50 -#, fuzzy msgid "Dejected" -msgstr "Eliminar" +msgstr "Desanimado"
#: ../src/common/pep.py:51 msgid "Depressed" -msgstr "" +msgstr "Deprimido"
#: ../src/common/pep.py:52 -#, fuzzy msgid "Disappointed" -msgstr "Deshabilitado" +msgstr "Decepcionado"
#: ../src/common/pep.py:53 msgid "Disgusted" -msgstr "" +msgstr "Disgustado"
#: ../src/common/pep.py:54 -#, fuzzy msgid "Dismayed" -msgstr "Deshabilitado" +msgstr "Consternado"
#: ../src/common/pep.py:55 -#, fuzzy msgid "Distracted" -msgstr "Deshabilitado" +msgstr "Distraído"
#: ../src/common/pep.py:56 msgid "Embarrassed" -msgstr "" +msgstr "Vergonzoso"
#: ../src/common/pep.py:57 -#, fuzzy msgid "Envious" -msgstr "_Anterior" +msgstr "Envidioso"
#: ../src/common/pep.py:58 msgid "Excited" -msgstr "" +msgstr "Excitado"
#: ../src/common/pep.py:59 msgid "Flirtatious" -msgstr "" +msgstr "Flirteador"
#: ../src/common/pep.py:60 msgid "Frustrated" -msgstr "" +msgstr "Frustrado"
#: ../src/common/pep.py:61 msgid "Grateful" -msgstr "" +msgstr "Agradecido"
#: ../src/common/pep.py:62 msgid "Grieving" -msgstr "" +msgstr "Dolido"
#: ../src/common/pep.py:63 -#, fuzzy msgid "Grumpy" -msgstr "Grupo" +msgstr "Malhumorado"
#: ../src/common/pep.py:64 msgid "Guilty" -msgstr "" +msgstr "Culpable"
#: ../src/common/pep.py:65 msgid "Happy" -msgstr "" +msgstr "Feliz"
#: ../src/common/pep.py:66 msgid "Hopeful" -msgstr "" +msgstr "Deseoso"
#: ../src/common/pep.py:67 -#, fuzzy msgid "Hot" -msgstr "_Servidor" +msgstr "Caliente"
#: ../src/common/pep.py:68 msgid "Humbled" -msgstr "" +msgstr "Humilde"
#: ../src/common/pep.py:69 msgid "Humiliated" -msgstr "" +msgstr "Humillado"
#: ../src/common/pep.py:70 msgid "Hungry" -msgstr "" +msgstr "Hambriento"
#: ../src/common/pep.py:71 msgid "Hurt" -msgstr "" +msgstr "Dolido"
#: ../src/common/pep.py:72 -#, fuzzy msgid "Impressed" -msgstr "mensaje" +msgstr "Impresionado"
#: ../src/common/pep.py:73 msgid "In Awe" -msgstr "" +msgstr "Atemorizado"
#: ../src/common/pep.py:74 msgid "In Love" -msgstr "" +msgstr "Enamorado"
#: ../src/common/pep.py:75 msgid "Indignant" -msgstr "" +msgstr "Indignado"
#: ../src/common/pep.py:76 msgid "Interested" -msgstr "" +msgstr "Curioso"
#: ../src/common/pep.py:77 msgid "Intoxicated" -msgstr "" +msgstr "Intoxicado"
#: ../src/common/pep.py:78 -#, fuzzy msgid "Invincible" -msgstr "Invisible" +msgstr "Invencible"
#: ../src/common/pep.py:79 msgid "Jealous" -msgstr "" +msgstr "Celoso"
#: ../src/common/pep.py:80 -#, fuzzy msgid "Lonely" -msgstr "uno" +msgstr "Solitario"
#: ../src/common/pep.py:81 -#, fuzzy msgid "Lost" -msgstr "_Servidor" +msgstr "Perdido"
#: ../src/common/pep.py:82 msgid "Lucky" -msgstr "" +msgstr "Afortunado"
#: ../src/common/pep.py:83 -#, fuzzy msgid "Mean" -msgstr "Alemán" +msgstr "Significante"
#: ../src/common/pep.py:84 -#, fuzzy msgid "Moody" -msgstr "_Modificar" +msgstr "Malhumorado"
#: ../src/common/pep.py:85 -#, fuzzy msgid "Nervous" -msgstr "_Anterior" +msgstr "Nervioso"
#: ../src/common/pep.py:86 msgid "Neutral" -msgstr "" +msgstr "Neutral"
#: ../src/common/pep.py:87 -#, fuzzy msgid "Offended" -msgstr "Desconectado" +msgstr "Ofendido"
#: ../src/common/pep.py:88 msgid "Outraged" -msgstr "" +msgstr "Escandalizado"
#: ../src/common/pep.py:89 msgid "Playful" -msgstr "" +msgstr "Juguetón"
#: ../src/common/pep.py:90 -#, fuzzy msgid "Proud" -msgstr "Grupo" +msgstr "Orgulloso"
#: ../src/common/pep.py:91 msgid "Relaxed" -msgstr "" +msgstr "Relajado"
#: ../src/common/pep.py:92 -#, fuzzy msgid "Relieved" -msgstr "once" +msgstr "Aliviado"
#: ../src/common/pep.py:93 msgid "Remorseful" -msgstr "" +msgstr "Arrepentido"
#: ../src/common/pep.py:94 msgid "Restless" -msgstr "" +msgstr "Inquieto"
#: ../src/common/pep.py:95 -#, fuzzy msgid "Sad" -msgstr "Atascado" +msgstr "Triste"
#: ../src/common/pep.py:96 msgid "Sarcastic" -msgstr "" +msgstr "Sarcástico"
#: ../src/common/pep.py:97 -#, fuzzy msgid "Satisfied" -msgstr "Última modificación:" +msgstr "Satisfecho"
#: ../src/common/pep.py:98 -#, fuzzy msgid "Serious" -msgstr "_Anterior" +msgstr "Serio"
#: ../src/common/pep.py:99 msgid "Shocked" -msgstr "" +msgstr "Horrorizado"
#: ../src/common/pep.py:100 msgid "Shy" -msgstr "" +msgstr "Tímido"
#: ../src/common/pep.py:101 -#, fuzzy msgid "Sick" -msgstr "Alias" +msgstr "Enfermo"
#: ../src/common/pep.py:102 -#, fuzzy msgid "Sleepy" -msgstr "Durmiendo" +msgstr "Adormilado"
#: ../src/common/pep.py:103 msgid "Spontaneous" -msgstr "" +msgstr "Espontáneo"
#: ../src/common/pep.py:104 -#, fuzzy msgid "Stressed" -msgstr "Calle:" +msgstr "Estresado"
#: ../src/common/pep.py:105 msgid "Strong" -msgstr "" +msgstr "Fuerte"
#: ../src/common/pep.py:106 -#, fuzzy msgid "Surprised" -msgstr "Añadido" +msgstr "Sorprendido"
#: ../src/common/pep.py:107 msgid "Thankful" -msgstr "" +msgstr "Agradecido"
#: ../src/common/pep.py:108 msgid "Thirsty" -msgstr "" +msgstr "Sediento"
#: ../src/common/pep.py:109 -#, fuzzy msgid "Tired" -msgstr "Hora" +msgstr "Cansado"
#: ../src/common/pep.py:110 -#, fuzzy msgid "Undefined" -msgstr "Subrayado" +msgstr "Indefinido"
#: ../src/common/pep.py:111 msgid "Weak" -msgstr "" +msgstr "Débil"
#: ../src/common/pep.py:112 msgid "Worried" -msgstr "" +msgstr "Preocupado"
#: ../src/common/pep.py:115 -#, fuzzy msgid "Doing Chores" -msgstr "Host erróneo" +msgstr "Haciendo tareas"
#: ../src/common/pep.py:116 msgid "Buying Groceries" -msgstr "" +msgstr "Haciendo la compra"
#: ../src/common/pep.py:117 -#, fuzzy msgid "Cleaning" -msgstr "Noche" +msgstr "Limpiando"
#: ../src/common/pep.py:118 -#, fuzzy msgid "Cooking" -msgstr "Componiendo" +msgstr "Cocinando"
#: ../src/common/pep.py:119 msgid "Doing Maintenance" -msgstr "" +msgstr "Tareas de mantenimiento"
#: ../src/common/pep.py:120 msgid "Doing the Dishes" -msgstr "" +msgstr "Fregando los platos"
#: ../src/common/pep.py:121 msgid "Doing the Laundry" -msgstr "" +msgstr "Haciendo la colada"
#: ../src/common/pep.py:122 -#, fuzzy msgid "Gardening" -msgstr "Mañana" +msgstr "Arreglando el jardín"
#: ../src/common/pep.py:123 msgid "Running an Errand" -msgstr "" +msgstr "Haciendo un recado"
#: ../src/common/pep.py:124 -#, fuzzy msgid "Walking the Dog" -msgstr "todos en el grupo" +msgstr "Paseando al perro"
#: ../src/common/pep.py:125 -#, fuzzy msgid "Drinking" -msgstr "Trabajando" +msgstr "Bebiendo"
#: ../src/common/pep.py:126 msgid "Having a Beer" -msgstr "" +msgstr "Tomando una cerveza"
#: ../src/common/pep.py:127 msgid "Having Coffee" -msgstr "" +msgstr "Tomando café"
#: ../src/common/pep.py:128 msgid "Having Tea" -msgstr "" +msgstr "Tomando té"
#: ../src/common/pep.py:130 msgid "Having a Snack" -msgstr "" +msgstr "Tomando un tentempié"
#: ../src/common/pep.py:131 msgid "Having Breakfast" -msgstr "" +msgstr "Desayunando"
#: ../src/common/pep.py:132 msgid "Having Dinner" -msgstr "" +msgstr "Cenando"
#: ../src/common/pep.py:133 msgid "Having Lunch" -msgstr "" +msgstr "Almorzando"
#: ../src/common/pep.py:134 msgid "Exercising" -msgstr "" +msgstr "Haciendo ejercicio"
#: ../src/common/pep.py:135 ../src/common/pep.py:180 msgid "Cycling" -msgstr "" +msgstr "Montando en bici"
#: ../src/common/pep.py:136 -#, fuzzy msgid "Dancing" -msgstr "Noche" +msgstr "Bailando"
#: ../src/common/pep.py:137 -#, fuzzy msgid "Hiking" -msgstr "Expulsando a %s" +msgstr "Paseando"
#: ../src/common/pep.py:138 -#, fuzzy msgid "Jogging" -msgstr "_Entrar" +msgstr "Corriendo"
#: ../src/common/pep.py:139 msgid "Playing Sports" -msgstr "" +msgstr "Haciendo deporte"
#: ../src/common/pep.py:140 msgid "Running" -msgstr "" +msgstr "Corriendo"
#: ../src/common/pep.py:141 -#, fuzzy msgid "Skiing" -msgstr "Trabajando" +msgstr "Esquiando"
#: ../src/common/pep.py:142 msgid "Swimming" -msgstr "" +msgstr "Nadando"
#: ../src/common/pep.py:143 -#, fuzzy msgid "Working out" msgstr "Trabajando"
#: ../src/common/pep.py:144 msgid "Grooming" -msgstr "" +msgstr "Lavando"
#: ../src/common/pep.py:145 msgid "At the Spa" -msgstr "" +msgstr "En el spa"
#: ../src/common/pep.py:146 msgid "Brushing Teeth" -msgstr "" +msgstr "Lavando los dientes"
#: ../src/common/pep.py:147 msgid "Getting a Haircut" -msgstr "" +msgstr "Cortando el pelo"
#: ../src/common/pep.py:148 -#, fuzzy msgid "Shaving" -msgstr "Comiendo" +msgstr "Afeitándome"
#: ../src/common/pep.py:149 msgid "Taking a Bath" -msgstr "" +msgstr "Tomando un baño"
#: ../src/common/pep.py:150 msgid "Taking a Shower" -msgstr "" +msgstr "Duchándome"
#: ../src/common/pep.py:151 msgid "Having an Appointment" -msgstr "" +msgstr "En una cita"
#: ../src/common/pep.py:153 msgid "Day Off" -msgstr "" +msgstr "Fin del día"
#: ../src/common/pep.py:154 -#, fuzzy msgid "Hanging out" -msgstr "Cambiando el tema" +msgstr "Saliendo por ahi"
#: ../src/common/pep.py:155 -#, fuzzy msgid "Hiding" -msgstr "Expulsando a %s" +msgstr "Escondiéndome"
#: ../src/common/pep.py:156 msgid "On Vacation" -msgstr "" +msgstr "De vacaciones"
#: ../src/common/pep.py:157 -#, fuzzy msgid "Praying" -msgstr "Comiendo" +msgstr "Rezando"
#: ../src/common/pep.py:158 msgid "Scheduled Holiday" -msgstr "" +msgstr "Vacaciones planificadas"
#: ../src/common/pep.py:160 -#, fuzzy msgid "Thinking" -msgstr "Trabajando" +msgstr "Pensando"
#: ../src/common/pep.py:161 msgid "Relaxing" -msgstr "" +msgstr "Relajándome"
#: ../src/common/pep.py:162 -#, fuzzy msgid "Fishing" -msgstr "Expulsando a %s" +msgstr "Pescando"
#: ../src/common/pep.py:163 -#, fuzzy msgid "Gaming" -msgstr "Comiendo" +msgstr "Jugando"
#: ../src/common/pep.py:164 -#, fuzzy msgid "Going out" -msgstr "_Desconectar" +msgstr "Saliendo"
#: ../src/common/pep.py:165 -#, fuzzy msgid "Partying" -msgstr "Comiendo" +msgstr "De fiesta"
#: ../src/common/pep.py:166 -#, fuzzy msgid "Reading" -msgstr "Motivo" +msgstr "Leyendo"
#: ../src/common/pep.py:167 -#, fuzzy msgid "Rehearsing" -msgstr "Motivo" +msgstr "Ensayando"
#: ../src/common/pep.py:168 -#, fuzzy msgid "Shopping" -msgstr "Durmiendo" +msgstr "Comprando"
#: ../src/common/pep.py:169 -#, fuzzy msgid "Smoking" -msgstr "Trabajando" +msgstr "Fumando"
#: ../src/common/pep.py:170 msgid "Socializing" -msgstr "" +msgstr "Socializándome"
#: ../src/common/pep.py:171 -#, fuzzy msgid "Sunbathing" -msgstr "Comiendo" +msgstr "En el solarium"
#: ../src/common/pep.py:172 msgid "Watching TV" -msgstr "" +msgstr "Viendo la TV"
#: ../src/common/pep.py:173 -#, fuzzy msgid "Watching a Movie" -msgstr "Estoy viendo una película." +msgstr "Viendo una película"
#: ../src/common/pep.py:174 -#, fuzzy msgid "Talking" -msgstr "Comiendo" +msgstr "Hablando"
#: ../src/common/pep.py:175 msgid "In Real Life" -msgstr "" +msgstr "En la vida real"
#: ../src/common/pep.py:176 -#, fuzzy msgid "On the Phone" -msgstr "Estoy al teléfono." +msgstr "Al teléfono"
#: ../src/common/pep.py:177 msgid "On Video Phone" -msgstr "" +msgstr "Al videoteléfono"
#: ../src/common/pep.py:178 -#, fuzzy msgid "Traveling" -msgstr "Transfiriendo" +msgstr "Viajando"
#: ../src/common/pep.py:179 -#, fuzzy msgid "Commuting" -msgstr "Componiendo" +msgstr "Conmutar"
#: ../src/common/pep.py:181 msgid "Driving" -msgstr "" +msgstr "Conduciendo"
#: ../src/common/pep.py:182 msgid "In a Car" -msgstr "" +msgstr "En el coche"
#: ../src/common/pep.py:183 msgid "On a Bus" -msgstr "" +msgstr "En el bus"
#: ../src/common/pep.py:184 msgid "On a Plane" -msgstr "" +msgstr "En el avión"
#: ../src/common/pep.py:185 -#, fuzzy msgid "On a Train" -msgstr "_Abrir como enlace" +msgstr "En el tren"
#: ../src/common/pep.py:186 msgid "On a Trip" -msgstr "" +msgstr "En un viaje"
#: ../src/common/pep.py:187 -#, fuzzy msgid "Walking" -msgstr "Trabajando" +msgstr "Caminando"
#: ../src/common/pep.py:189 -#, fuzzy msgid "Coding" -msgstr "Componiendo" +msgstr "Programando"
#: ../src/common/pep.py:190 msgid "In a Meeting" -msgstr "" +msgstr "En un encuentro"
#: ../src/common/pep.py:191 msgid "Studying" -msgstr "" +msgstr "Estudiando"
#: ../src/common/pep.py:192 -#, fuzzy msgid "Writing" -msgstr "Trabajando" +msgstr "Escribiendo"
#. We cannot bind port, call error callback and fail -#: ../src/common/socks5.py:84 +#: ../src/common/socks5.py:86 #, python-format msgid "Unable to bind to port %s." msgstr "No se puede enlazar al puerto %s."
-#: ../src/common/socks5.py:85 -#, fuzzy +#: ../src/common/socks5.py:87 msgid "" "Maybe you have another running instance of Gajim. File Transfer will be " "cancelled." @@ -5789,37 +5672,32 @@ "cancelada."
#. we're not english -#: ../src/common/stanza_session.py:278 +#: ../src/common/stanza_session.py:279 msgid "" "[This is part of an encrypted session. If you see this message, something " "went wrong.]" msgstr "" +"[Esto es parte de una sesión cifrada. Si ves este mensaje, algo fue mal.]"
-#: ../src/common/zeroconf/client_zeroconf.py:408 +#: ../src/common/zeroconf/connection_handlers_zeroconf.py:90 +#, python-format msgid "" -"Connection to host could not be established: Timeout while sending data." -msgstr "" -"No se pudo establecer la conexión al host: Expiró enviando información." - -#: ../src/common/zeroconf/connection_handlers_zeroconf.py:93 -#, fuzzy, python-format -msgid "" "The host %s you configured as the ft_add_hosts_to_send advanced option is " "not valid, so ignored." msgstr "" -"El host que configuraste como la opción avanzada ft_override_host_to_send no " -"es válido, será ignorado." +"El host %s que configuraste como la opción avanzada ft_override_host_to_send " +"no es válido, será ignorado."
-#: ../src/common/zeroconf/connection_zeroconf.py:215 +#: ../src/common/zeroconf/connection_zeroconf.py:216 msgid "To continue sending and receiving messages, you will need to reconnect." msgstr "" "Para continuar enviando y reciviendo mensajes, necesitarás volver a conectar."
-#: ../src/common/zeroconf/connection_zeroconf.py:238 +#: ../src/common/zeroconf/connection_zeroconf.py:239 msgid "Avahi error" msgstr "Error de Avahi"
-#: ../src/common/zeroconf/connection_zeroconf.py:238 +#: ../src/common/zeroconf/connection_zeroconf.py:239 #, python-format msgid "" "%s\n" @@ -5828,32 +5706,31 @@ "%s\n" "La mensajería de enlace local podría no funcionar correctamente."
-#: ../src/common/zeroconf/connection_zeroconf.py:249 -#, fuzzy +#: ../src/common/zeroconf/connection_zeroconf.py:250 msgid "Please check if Avahi or Bonjour is installed." -msgstr "Por favor, comprueba si Avahi está instalado." +msgstr "Por favor, comprueba si Avahi o Bonjour están instalados."
-#: ../src/common/zeroconf/connection_zeroconf.py:258 -#: ../src/common/zeroconf/connection_zeroconf.py:262 +#: ../src/common/zeroconf/connection_zeroconf.py:259 +#: ../src/common/zeroconf/connection_zeroconf.py:263 msgid "Could not start local service" msgstr "No se pudo iniciar servicio local"
-#: ../src/common/zeroconf/connection_zeroconf.py:259 +#: ../src/common/zeroconf/connection_zeroconf.py:260 #, python-format msgid "Unable to bind to port %d." msgstr "No se pudo enlazar al puerto %d."
-#: ../src/common/zeroconf/connection_zeroconf.py:263 -#: ../src/common/zeroconf/connection_zeroconf.py:358 +#: ../src/common/zeroconf/connection_zeroconf.py:264 +#: ../src/common/zeroconf/connection_zeroconf.py:359 msgid "Please check if avahi-daemon is running." msgstr "Por favor, comprueba si avahi-daemon se encuentra en ejecución."
-#: ../src/common/zeroconf/connection_zeroconf.py:357 +#: ../src/common/zeroconf/connection_zeroconf.py:358 #, python-format msgid "Could not change status of account "%s"" msgstr "No se pudo cambiar el estado de la cuenta "%s""
-#: ../src/common/zeroconf/connection_zeroconf.py:374 +#: ../src/common/zeroconf/connection_zeroconf.py:381 msgid "" "You are not connected or not visible to others. Your message could not be " "sent." @@ -5862,63 +5739,65 @@ "enviado."
#. we're not english -#: ../src/common/zeroconf/connection_zeroconf.py:391 +#: ../src/common/zeroconf/connection_zeroconf.py:399 msgid "[This message is encrypted]" -msgstr "[Este mensaje está encriptado]" +msgstr "[Este mensaje está cifrado]"
-#: ../src/common/zeroconf/connection_zeroconf.py:457 -#, fuzzy +#: ../src/common/zeroconf/connection_zeroconf.py:480 msgid "Your message could not be sent." -msgstr "El contacto está desconectado. Tu mensaje no pudo ser enviado." +msgstr "Tu mensaje no pudo ser enviado."
#. Contact Offline -#: ../src/common/zeroconf/connection_zeroconf.py:464 +#: ../src/common/zeroconf/connection_zeroconf.py:486 msgid "Contact is offline. Your message could not be sent." msgstr "El contacto está desconectado. Tu mensaje no pudo ser enviado."
+#: ../src/common/zeroconf/connection_zeroconf.py:585 +msgid "" +"Connection to host could not be established: Timeout while sending data." +msgstr "" +"No se pudo establecer la conexión al host: Expiró enviando información." + #: ../src/common/zeroconf/zeroconf_avahi.py:183 -#: ../src/common/zeroconf/zeroconf_bonjour.py:194 +#: ../src/common/zeroconf/zeroconf_bonjour.py:191 #, python-format msgid "Error while adding service. %s" msgstr "Error añadiendo el servicio. %s"
-#: ../src/config.py:150 ../src/config.py:582 +#: ../src/config.py:150 ../src/config.py:579 msgid "Disabled" msgstr "Deshabilitado"
-#: ../src/config.py:321 +#: ../src/config.py:318 msgid "Active" msgstr "Activo"
-#: ../src/config.py:329 +#: ../src/config.py:326 msgid "Event" msgstr "Evento"
-#: ../src/config.py:405 -#, fuzzy +#: ../src/config.py:402 msgid "Default Message" -msgstr "Mensajes de estado por defecto" +msgstr "Mensajes por defecto"
-#: ../src/config.py:412 -#, fuzzy +#: ../src/config.py:409 msgid "Enabled" -msgstr "Activar" +msgstr "Activado"
-#: ../src/config.py:454 +#: ../src/config.py:451 msgid "Always use OS/X default applications" -msgstr "" +msgstr "¿Usar siempre las aplicaciones por defecto de OS/X?"
-#: ../src/config.py:455 -#, fuzzy +#: ../src/config.py:452 msgid "Custom" -msgstr "<b>Personalizado</b>" +msgstr "Personalizado"
-#: ../src/config.py:650 ../src/dialogs.py:1209 +#: ../src/config.py:645 ../src/dialogs.py:1199 #, python-format msgid "Dictionary for lang %s not available" msgstr "No está disponible el diccionario para el idioma %s"
-#: ../src/config.py:651 +#: ../src/config.py:646 #, python-format msgid "" "You have to install %s dictionary to use spellchecking, or choose another " @@ -5927,111 +5806,110 @@ "Tienes que instalar el diccionario %s para usar el corrector, o elegir otro " "lenguaje definiendo la opción speller_language."
-#: ../src/config.py:1016 +#: ../src/config.py:1011 msgid "status message title" msgstr "título del mensaje de estado"
-#: ../src/config.py:1016 +#: ../src/config.py:1011 msgid "status message text" msgstr "texto del mensaje de estado"
-#: ../src/config.py:1053 +#: ../src/config.py:1047 msgid "First Message Received" msgstr "Primer mensaje recibido"
-#: ../src/config.py:1054 -#, fuzzy +#: ../src/config.py:1048 msgid "Next Message Received Focused" -msgstr "Nuevo mensaje recibido" +msgstr "Primer mensaje recibido focalizado"
-#: ../src/config.py:1056 -#, fuzzy +#: ../src/config.py:1050 msgid "Next Message Received Unfocused" -msgstr "Nuevo mensaje recibido" +msgstr "Primer mensaje recibido desfocalizado"
-#: ../src/config.py:1057 +#: ../src/config.py:1051 msgid "Contact Connected" msgstr "Nombre de contacto"
-#: ../src/config.py:1058 +#: ../src/config.py:1052 msgid "Contact Disconnected" msgstr "Contacto desconectado"
-#: ../src/config.py:1059 +#: ../src/config.py:1053 msgid "Message Sent" msgstr "Mensaje enviado"
-#: ../src/config.py:1060 +#: ../src/config.py:1054 msgid "Group Chat Message Highlight" msgstr "Resaltado de mensaje de grupo de charla"
-#: ../src/config.py:1061 +#: ../src/config.py:1055 msgid "Group Chat Message Received" msgstr "Mensaje de grupo de charla recibido"
-#: ../src/config.py:1062 +#: ../src/config.py:1056 msgid "GMail Email Received" msgstr "Correo de GMail recibido"
#. Name column -#: ../src/config.py:1319 ../src/dialogs.py:1833 ../src/dialogs.py:1897 -#: ../src/disco.py:768 ../src/disco.py:1562 ../src/disco.py:1809 +#: ../src/config.py:1373 ../src/dialogs.py:1823 ../src/dialogs.py:1887 +#: ../src/disco.py:770 ../src/disco.py:1562 ../src/disco.py:1808 #: ../src/history_window.py:87 msgid "Name" msgstr "Nombre"
-#: ../src/config.py:1405 +#: ../src/config.py:1459 msgid "Relogin now?" msgstr "¿Reconectar ahora?"
-#: ../src/config.py:1406 +#: ../src/config.py:1460 msgid "If you want all the changes to apply instantly, you must relogin." msgstr "" "Si quieres que se apliquen todos los cambios instantáneamente, debes " "reconectar."
-#: ../src/config.py:1531 ../src/config.py:1630 -msgid "OpenPGP is not usable in this computer" +#: ../src/config.py:1585 ../src/config.py:1684 +#, fuzzy +msgid "OpenPGP is not usable on this computer" msgstr "GPG no es usable en este ordenador"
-#: ../src/config.py:1666 ../src/config.py:1709 +#: ../src/config.py:1720 ../src/config.py:1763 msgid "Unread events" msgstr "Eventos sin leer"
-#: ../src/config.py:1667 +#: ../src/config.py:1721 msgid "Read all pending events before removing this account." msgstr "Lee todos los eventos pendientes antes de eliminar esta cuenta."
-#: ../src/config.py:1693 +#: ../src/config.py:1747 #, python-format msgid "You have opened chat in account %s" msgstr "Has abierto una conversación con la cuenta %s"
-#: ../src/config.py:1694 +#: ../src/config.py:1748 msgid "All chat and groupchat windows will be closed. Do you want to continue?" msgstr "" "Todas las conversaciones y grupos de charla serán cerrados. ¿Deseas " "continuar?"
-#: ../src/config.py:1705 +#: ../src/config.py:1759 msgid "You are currently connected to the server" msgstr "Debes estar conectado para navegar los servicios"
-#: ../src/config.py:1706 +#: ../src/config.py:1760 msgid "To change the account name, you must be disconnected." msgstr "Para cambiar el nombre de la cuenta, debes estar desconectado"
-#: ../src/config.py:1710 +#: ../src/config.py:1764 msgid "To change the account name, you must read all pending events." msgstr "" "Para cambiar el nombre de la cuenta, debes leer el resto de\n" " eventos pendientes"
-#: ../src/config.py:1716 +#: ../src/config.py:1770 msgid "Account Name Already Used" msgstr "El nombre de la cuenta ya está en uso"
-#: ../src/config.py:1717 +#: ../src/config.py:1771 msgid "" "This name is already used by another of your accounts. Please choose another " "name." @@ -6039,147 +5917,141 @@ "Este nombre ya está en uso por otra de tus cuentas. Por favor, escoge otro " "nombre."
-#: ../src/config.py:1721 ../src/config.py:1725 +#: ../src/config.py:1775 ../src/config.py:1779 msgid "Invalid account name" msgstr "Nombre de cuenta no válido"
-#: ../src/config.py:1722 +#: ../src/config.py:1776 msgid "Account name cannot be empty." msgstr "El nombre de la cuenta no puede estar vacío."
-#: ../src/config.py:1726 +#: ../src/config.py:1780 msgid "Account name cannot contain spaces." msgstr "El nombre de la cuenta no puede contener espacios."
-#: ../src/config.py:1800 -#, fuzzy +#: ../src/config.py:1854 msgid "Rename Account" -msgstr "Gestionar cuentas" +msgstr "Renombrar cuenta"
-#: ../src/config.py:1801 -#, fuzzy, python-format +#: ../src/config.py:1855 +#, python-format msgid "Enter a new name for account %s" -msgstr "Introduce un nuevo nombre para el grupo %s" +msgstr "Introduce un nuevo nombre para la cuenta %s"
-#: ../src/config.py:1819 ../src/config.py:1827 ../src/config.py:1870 -#: ../src/config.py:3089 ../src/dataforms_widget.py:536 -msgid "Invalid Jabber ID" -msgstr "ID de Jabber no válida" - -#: ../src/config.py:1828 +#: ../src/config.py:1882 msgid "A Jabber ID must be in the form "user@servername"." msgstr "Una ID de Jabber debe estar en la forma "usuario@nombreservidor"."
-#: ../src/config.py:2018 ../src/config.py:3161 +#: ../src/config.py:2072 ../src/config.py:3211 msgid "Invalid entry" msgstr "Entrada no válida"
-#: ../src/config.py:2019 ../src/config.py:3162 +#: ../src/config.py:2073 ../src/config.py:3212 msgid "Custom port must be a port number." msgstr "El puerto personalizado debe ser un número de puerto"
-#: ../src/config.py:2040 +#: ../src/config.py:2094 msgid "Failed to get secret keys" msgstr "Error obteniendo las claves secretas"
-#: ../src/config.py:2041 +#: ../src/config.py:2095 msgid "There was a problem retrieving your OpenPGP secret keys." msgstr "Ha habido un problema recuperando tus claves secretas OpenPGP"
-#: ../src/config.py:2076 +#: ../src/config.py:2130 msgid "OpenPGP Key Selection" msgstr "Selección de clave OpenPGP"
-#: ../src/config.py:2077 +#: ../src/config.py:2131 msgid "Choose your OpenPGP key" msgstr "Elige tu clave OpenPGP"
-#: ../src/config.py:2088 +#: ../src/config.py:2138 msgid "No such account available" msgstr "No está disponible dicha cuenta"
-#: ../src/config.py:2089 +#: ../src/config.py:2139 msgid "You must create your account before editing your personal information." msgstr "Debes crear una cuenta antes de editar tu información personal"
-#: ../src/config.py:2096 ../src/dialogs.py:1685 ../src/dialogs.py:1821 -#: ../src/dialogs.py:2000 ../src/disco.py:439 ../src/profile_window.py:320 +#: ../src/config.py:2146 ../src/dialogs.py:1675 ../src/dialogs.py:1811 +#: ../src/dialogs.py:1990 ../src/disco.py:439 ../src/profile_window.py:317 msgid "You are not connected to the server" msgstr "No estás conectado al servidor"
-#: ../src/config.py:2097 +#: ../src/config.py:2147 msgid "Without a connection, you can not edit your personal information." msgstr "Sin una conexión, no puedes editar tu información personal"
-#: ../src/config.py:2101 +#: ../src/config.py:2151 msgid "Your server doesn't support Vcard" msgstr "Tu servidor no soporta vCard"
-#: ../src/config.py:2102 +#: ../src/config.py:2152 msgid "Your server can't save your personal information." msgstr "Tu servidor no puede guardar tu información personal."
-#: ../src/config.py:2133 +#: ../src/config.py:2183 msgid "Account Local already exists." msgstr "La cuenta local ya existe."
-#: ../src/config.py:2134 +#: ../src/config.py:2184 msgid "Please rename or remove it before enabling link-local messaging." msgstr "" "Por favor, renómbralo o elimínalo antes de habilitar la mensajería de enlace " "local."
-#: ../src/config.py:2317 +#: ../src/config.py:2367 #, python-format msgid "Edit %s" msgstr "Editar %s"
-#: ../src/config.py:2319 +#: ../src/config.py:2369 #, python-format msgid "Register to %s" msgstr "Registrar a %s"
#. list at the beginning -#: ../src/config.py:2355 +#: ../src/config.py:2405 msgid "Ban List" msgstr "Lista de expulsión"
-#: ../src/config.py:2356 +#: ../src/config.py:2406 msgid "Member List" msgstr "Lista de miembros"
-#: ../src/config.py:2357 +#: ../src/config.py:2407 msgid "Owner List" msgstr "Lista de propietarios"
-#: ../src/config.py:2358 +#: ../src/config.py:2408 msgid "Administrator List" msgstr "Lista de administradores"
#. Address column #. holds JID (who said this) -#: ../src/config.py:2407 ../src/disco.py:775 ../src/history_manager.py:184 +#: ../src/config.py:2457 ../src/disco.py:777 ../src/history_manager.py:184 msgid "JID" msgstr ""
-#: ../src/config.py:2415 +#: ../src/config.py:2465 msgid "Reason" msgstr "Motivo"
-#: ../src/config.py:2420 +#: ../src/config.py:2470 msgid "Nick" msgstr "Alias"
-#: ../src/config.py:2424 +#: ../src/config.py:2474 msgid "Role" msgstr "Puesto"
-#: ../src/config.py:2449 +#: ../src/config.py:2499 msgid "Banning..." msgstr "Expulsando..."
#. You can move '\n' before user@domain if that line is TOO BIG -#: ../src/config.py:2451 +#: ../src/config.py:2501 msgid "" "<b>Whom do you want to ban?</b>\n" "\n" @@ -6187,11 +6059,11 @@ "<b>¿A quién quieres expulsar?</b>\n" "\n"
-#: ../src/config.py:2453 +#: ../src/config.py:2503 msgid "Adding Member..." msgstr "Añadiendo miembro..."
-#: ../src/config.py:2454 +#: ../src/config.py:2504 msgid "" "<b>Whom do you want to make a member?</b>\n" "\n" @@ -6199,12 +6071,11 @@ "<b>¿A quién quieres hacer miembro?</b>\n" "\n"
-#: ../src/config.py:2456 +#: ../src/config.py:2506 msgid "Adding Owner..." msgstr "Añadiendo propietario..."
-#: ../src/config.py:2457 -#, fuzzy +#: ../src/config.py:2507 msgid "" "<b>Whom do you want to make an owner?</b>\n" "\n" @@ -6212,11 +6083,11 @@ "<b>¿A quién quieres hacer propietario?</b>\n" "\n"
-#: ../src/config.py:2459 +#: ../src/config.py:2509 msgid "Adding Administrator..." msgstr "Añadiendo administrador..."
-#: ../src/config.py:2460 +#: ../src/config.py:2510 msgid "" "<b>Whom do you want to make an administrator?</b>\n" "\n" @@ -6224,7 +6095,7 @@ "<b>¿A quién quieres hacer administrador?</b>\n" "\n"
-#: ../src/config.py:2461 +#: ../src/config.py:2511 msgid "" "Can be one of the following:\n" "1. user@domain/resource (only that resource matches).\n" @@ -6240,87 +6111,86 @@ "usuario@dominio, dominio/recurso o dirección conteniendo\n" "un subdominio."
-#: ../src/config.py:2558 +#: ../src/config.py:2608 #, python-format msgid "Removing %s account" msgstr "Eliminando la cuenta %s"
-#: ../src/config.py:2580 ../src/gajim.py:1424 ../src/roster_window.py:1943 +#: ../src/config.py:2630 ../src/gajim.py:1429 ../src/roster_window.py:1932 msgid "Password Required" msgstr "Contraseña requerida"
-#: ../src/config.py:2581 ../src/roster_window.py:1917 +#: ../src/config.py:2631 ../src/roster_window.py:1906 #, python-format msgid "Enter your password for account %s" msgstr "Introduce contraseña para la cuenta %s"
-#: ../src/config.py:2582 ../src/roster_window.py:1944 +#: ../src/config.py:2632 ../src/roster_window.py:1933 msgid "Save password" msgstr "Guardar contraseña"
-#: ../src/config.py:2591 +#: ../src/config.py:2641 #, python-format msgid "Account "%s" is connected to the server" msgstr "La cuenta "%s" está conectada al servidor"
-#: ../src/config.py:2592 +#: ../src/config.py:2642 msgid "If you remove it, the connection will be lost." msgstr "Si la eliminas, se perderá la conexión con el servidor"
-#: ../src/config.py:2688 +#: ../src/config.py:2738 msgid "Default" msgstr "Por defecto"
-#: ../src/config.py:2688 +#: ../src/config.py:2738 msgid "?print_status:All" msgstr "Todos"
-#: ../src/config.py:2689 +#: ../src/config.py:2739 msgid "Enter and leave only" msgstr "Entrar y salir sólamente"
-#: ../src/config.py:2690 +#: ../src/config.py:2740 #, fuzzy msgid "?print_status:None" msgstr "Todos"
-#: ../src/config.py:2758 +#: ../src/config.py:2808 msgid "New Group Chat" msgstr "Nuevo grupo de charla"
-#: ../src/config.py:2791 +#: ../src/config.py:2841 msgid "This bookmark has invalid data" msgstr "Este marcador tiene información no válida"
-#: ../src/config.py:2792 +#: ../src/config.py:2842 msgid "" "Please be sure to fill out server and room fields or remove this bookmark." msgstr "" "Por favor, asegúrate de rellenar los campos de servidor y salón o elimina " "este marcador."
-#: ../src/config.py:3072 +#: ../src/config.py:3122 msgid "Invalid username" msgstr "Nombre de usuario no válido"
-#: ../src/config.py:3074 +#: ../src/config.py:3124 msgid "You must provide a username to configure this account." msgstr "Debes proporcionar un nombre para configurar esta cuenta."
-#: ../src/config.py:3100 +#: ../src/config.py:3150 msgid "Duplicate Jabber ID" msgstr "ID de Jabber duplicada"
-#: ../src/config.py:3101 +#: ../src/config.py:3151 msgid "This account is already configured in Gajim." msgstr "Esta cuenta ya está configurada en Gajim."
-#: ../src/config.py:3118 +#: ../src/config.py:3168 msgid "Account has been added successfully" msgstr "La cuenta ha sido añadida con éxito"
-#: ../src/config.py:3119 ../src/config.py:3308 -#, fuzzy +#: ../src/config.py:3169 ../src/config.py:3358 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 " @@ -6330,27 +6200,24 @@ "Avanzado, o más tarde mediante el submenú Cuentas del menú Editar de la " "ventana principal."
-#: ../src/config.py:3137 -#, fuzzy +#: ../src/config.py:3187 msgid "Invalid server" -msgstr "Nombre de usuario no válido" +msgstr "Servidor no válido"
-#: ../src/config.py:3138 -#, fuzzy +#: ../src/config.py:3188 msgid "Please provide a server on which you want to register." -msgstr "Especifica el nuevo alias que quieres usar:" +msgstr "Por favor, especifica un servidor donde te quieras registrar."
-#: ../src/config.py:3189 ../src/gajim.py:1996 -#, fuzzy +#: ../src/config.py:3239 ../src/gajim.py:2007 msgid "Certificate Already in File" -msgstr "Contacto ya presente en el roster" +msgstr "El certificado ya está en el archivo"
-#: ../src/config.py:3190 ../src/gajim.py:1997 +#: ../src/config.py:3240 ../src/gajim.py:2008 #, python-format msgid "This certificate is already in file %s, so it's not added again." -msgstr "" +msgstr "Este certificado ya está en el archivo %s, no fue añadido de nuevo."
-#: ../src/config.py:3258 +#: ../src/config.py:3308 #, python-format msgid "" "<b>Security Warning</b>\n" @@ -6359,41 +6226,50 @@ "SSL Error: %(error)s\n" "Do you still want to connect to this server?" msgstr "" +"<b>Aviso de seguridad</b>\n" +"\n" +"La autenticidad del certificado SSL de %(hostname)s podría no ser válida.\n" +"Error SSL: %(error)s\n" +"¿Aún quieres conectar a este servidor?"
-#: ../src/config.py:3264 ../src/gajim.py:2020 +#: ../src/config.py:3314 ../src/gajim.py:2031 #, python-format msgid "" "Add this certificate to the list of trusted certificates.\n" "SHA1 fingerprint of the certificate:\n" "%s" msgstr "" +"Añadir este certificado a la lista de certificados de confianza.\n" +"Huella SHA1 del certificado:\n" +"%s"
-#: ../src/config.py:3287 ../src/config.py:3326 -#, fuzzy +#: ../src/config.py:3337 ../src/config.py:3376 msgid "An error occurred during account creation" msgstr "Ha ocurrido un error durante la creación de la cuenta"
-#: ../src/config.py:3307 +#: ../src/config.py:3357 msgid "Your new account has been created successfully" msgstr "Tu nueva cuenta ha sido creada con éxito"
-#: ../src/config.py:3410 +#: ../src/config.py:3460 msgid "Account name is in use" msgstr "El nombre de la cuenta está en uso"
-#: ../src/config.py:3411 +#: ../src/config.py:3461 msgid "You already have an account using this name." msgstr "Ya tienes una cuenta usando este nombre"
-#: ../src/conversation_textview.py:549 +#: ../src/conversation_textview.py:562 msgid "" "This icon indicates that this message has not yet\n" "been received by the remote end. If this icon stays\n" "for a long time, it's likely the message got lost." msgstr "" +"Este icono indica que este mensaje no ha sido todavía\n" +"recibido por el extremo remoto. Si este icono permanece\n" +"mucho tiempo, probablemente el mensaje se haya perdido."
-#: ../src/conversation_textview.py:568 -#, fuzzy +#: ../src/conversation_textview.py:581 msgid "" "Text below this line is what has been said since the\n" "last time you paid attention to this group chat" @@ -6401,78 +6277,77 @@ "El texto bajo esta línea es lo que ha sido dicho desde la última vez que " "prestaste atención a este salón de charla"
-#: ../src/conversation_textview.py:678 +#: ../src/conversation_textview.py:691 #, python-format msgid "_Actions for "%s"" msgstr "_Acciones para "%s""
-#: ../src/conversation_textview.py:690 +#: ../src/conversation_textview.py:703 msgid "Read _Wikipedia Article" msgstr "Leer artículo de _Wikipedia"
-#: ../src/conversation_textview.py:695 +#: ../src/conversation_textview.py:708 msgid "Look it up in _Dictionary" msgstr "Buscarlo en el _Diccionario"
-#: ../src/conversation_textview.py:712 +#: ../src/conversation_textview.py:725 #, python-format msgid "Dictionary URL is missing an "%s" and it is not WIKTIONARY" msgstr "Falta un "%s" en la URL del Diccionario y no está en WIKTIONARY"
#. we must have %s in the url -#: ../src/conversation_textview.py:725 +#: ../src/conversation_textview.py:738 #, python-format msgid "Web Search URL is missing an "%s"" msgstr "Falta un "%s" en la URL de la búsqueda web"
-#: ../src/conversation_textview.py:728 +#: ../src/conversation_textview.py:741 msgid "Web _Search for it" msgstr "_Buscarlo en la web"
-#: ../src/conversation_textview.py:734 +#: ../src/conversation_textview.py:747 msgid "Open as _Link" msgstr "_Abrir como enlace"
-#: ../src/conversation_textview.py:973 ../src/conversation_textview.py:985 +#: ../src/conversation_textview.py:1014 ../src/conversation_textview.py:1032 #, python-format msgid "Error executing "%(command)s": %(error)s" -msgstr "" +msgstr "Error ejecutando "%(command)s": %(error)s"
-#: ../src/conversation_textview.py:1238 +#: ../src/conversation_textview.py:1289 msgid "Yesterday" msgstr "Ayer"
#. the number is >= 2 #. %i is day in year (1-365), %d (1-31) we want %i -#: ../src/conversation_textview.py:1242 +#: ../src/conversation_textview.py:1293 #, python-format msgid "%i days ago" msgstr "hace %i días"
#. if we have subject, show it too! -#: ../src/conversation_textview.py:1276 +#: ../src/conversation_textview.py:1327 #, python-format msgid "Subject: %s\n" msgstr "Tema: %s\n"
#: ../src/dataforms_widget.py:540 -#, fuzzy msgid "Jabber ID already in list" -msgstr "Cliente de IM Jabber" +msgstr "La ID de Jabber ya está en la lista"
#: ../src/dataforms_widget.py:541 msgid "The Jabber ID you entered is already in the list. Choose another one." -msgstr "" +msgstr "La ID de Jabber que introdujiste ya está en la lista. Elige otra."
#. Default jid #: ../src/dataforms_widget.py:552 msgid "new@jabber.id" -msgstr "" +msgstr "nuevo@jabber.id"
#: ../src/dataforms_widget.py:555 ../src/dataforms_widget.py:557 #, python-format msgid "new%d@jabber.id" -msgstr "" +msgstr "nuevo%d@jabber.id"
#: ../src/dialogs.py:75 #, python-format @@ -6500,234 +6375,224 @@ msgid "Contact name" msgstr "Nombre de contacto"
-#: ../src/dialogs.py:461 -#, fuzzy +#: ../src/dialogs.py:460 msgid "Set Mood" -msgstr "Definir MOTD" +msgstr "Definir estado de ánimo"
-#: ../src/dialogs.py:558 +#: ../src/dialogs.py:548 #, python-format msgid "%s Status Message" msgstr "Mensaje de estado %s"
-#: ../src/dialogs.py:560 +#: ../src/dialogs.py:550 msgid "Status Message" msgstr "Mensaje de estado"
-#: ../src/dialogs.py:672 -#, fuzzy +#: ../src/dialogs.py:662 msgid "Overwrite Status Message?" -msgstr "Mensaje de estado" +msgstr "¿Sobreescribir mensaje de estado?"
-#: ../src/dialogs.py:673 -#, fuzzy +#: ../src/dialogs.py:663 msgid "" "This name is already used. Do you want to overwrite this status message?" -msgstr "" -"Este nombre ya está en uso por otra de tus cuentas. Por favor, escoge otro " -"nombre." +msgstr "Este nombre ya se usa. ¿Quieres sobreescribir este mensaje de estado?."
-#: ../src/dialogs.py:682 +#: ../src/dialogs.py:672 msgid "Save as Preset Status Message" msgstr "Guardar como mensaje de estado predefinido"
-#: ../src/dialogs.py:683 +#: ../src/dialogs.py:673 msgid "Please type a name for this status message" msgstr "Escribe un nombre para este mensaje de estado"
-#: ../src/dialogs.py:689 +#: ../src/dialogs.py:679 msgid "AIM Address:" msgstr "Dirección AIM:"
-#: ../src/dialogs.py:690 +#: ../src/dialogs.py:680 msgid "GG Number:" msgstr "Número GG:"
-#: ../src/dialogs.py:691 +#: ../src/dialogs.py:681 msgid "ICQ Number:" msgstr "Número ICQ:"
-#: ../src/dialogs.py:692 +#: ../src/dialogs.py:682 msgid "MSN Address:" msgstr "Dirección MSN:"
-#: ../src/dialogs.py:693 +#: ../src/dialogs.py:683 msgid "Yahoo! Address:" msgstr "Dirección Yahoo!:"
-#: ../src/dialogs.py:729 +#: ../src/dialogs.py:719 #, python-format msgid "Please fill in the data of the contact you want to add in account %s" msgstr "" "Por favor, introduce la inforamción del contacto que quieres añadir a la " "cuenta %s"
-#: ../src/dialogs.py:731 +#: ../src/dialogs.py:721 msgid "Please fill in the data of the contact you want to add" msgstr "Por favor, rellena la información del contacto que quieres añadir"
-#: ../src/dialogs.py:888 ../src/dialogs.py:894 ../src/dialogs.py:899 +#: ../src/dialogs.py:878 ../src/dialogs.py:884 ../src/dialogs.py:889 msgid "Invalid User ID" msgstr "ID de Usuario no válida"
-#: ../src/dialogs.py:895 +#: ../src/dialogs.py:885 msgid "The user ID must not contain a resource." msgstr "Esta ID de usuario no contiene un recurso."
-#: ../src/dialogs.py:900 -#, fuzzy +#: ../src/dialogs.py:890 msgid "You cannot add yourself to your roster." -msgstr "Me gustaría añadirte a mi lista de contactos" +msgstr "No puedes añadirte a tí mismo a tu roster."
-#: ../src/dialogs.py:914 +#: ../src/dialogs.py:904 msgid "Contact already in roster" msgstr "Contacto ya presente en el roster"
-#: ../src/dialogs.py:915 +#: ../src/dialogs.py:905 msgid "This contact is already listed in your roster." msgstr "Este contacto ya está listado en tu roster."
-#: ../src/dialogs.py:951 +#: ../src/dialogs.py:941 msgid "User ID:" msgstr "ID de usuario:"
-#: ../src/dialogs.py:1009 +#: ../src/dialogs.py:999 msgid "A GTK+ jabber client" msgstr "Un cliente de Jabber en GTK"
-#: ../src/dialogs.py:1010 +#: ../src/dialogs.py:1000 msgid "GTK+ Version:" msgstr "Versión de GTK+:"
-#: ../src/dialogs.py:1011 +#: ../src/dialogs.py:1001 msgid "PyGTK Version:" msgstr "Versión de PyGTK:"
-#: ../src/dialogs.py:1021 +#: ../src/dialogs.py:1011 msgid "Current Developers:" msgstr "Desarrolladores actuales:"
-#: ../src/dialogs.py:1023 +#: ../src/dialogs.py:1013 msgid "Past Developers:" msgstr "Desarrolladores anteriores:"
-#: ../src/dialogs.py:1029 +#: ../src/dialogs.py:1019 msgid "THANKS:" msgstr "AGRADECIMIENTOS:"
#. remove one english sentence #. and add it manually as translatable -#: ../src/dialogs.py:1035 +#: ../src/dialogs.py:1025 msgid "Last but not least, we would like to thank all the package maintainers." msgstr "" "Por último pero no menos importante, queremos agradecer a todos los " "mantenedores de paquetes."
#. here you write your name in the form Name FamilyName someone@somewhere -#: ../src/dialogs.py:1048 +#: ../src/dialogs.py:1038 msgid "translator-credits" -msgstr "Fran Alburquerque membriskhan@gmail.com" +msgstr "Fran Alburquerque frank@giingo.org"
-#: ../src/dialogs.py:1210 -#, fuzzy, python-format +#: ../src/dialogs.py:1200 +#, python-format msgid "" "You have to install %s dictionary to use spellchecking, or choose another " "language by setting the speller_language option.\n" "\n" "Highlighting misspelled words feature will not be used" msgstr "" -"Tienes que instalar el diccionario %s para usar el corrector, o elegir otro " -"lenguaje definiendo la opción speller_language." +"Tienes que instalar el diccionario %s para usar el corrector de escritura o " +"elegir otro idioma para el ajustar la opción speller_language option.\n" +"\n" +"No se usará el resaltado de los errores de escritura"
-#: ../src/dialogs.py:1614 -#, fuzzy, python-format +#: ../src/dialogs.py:1604 +#, python-format msgid "Subscription request for account %(account)s from %(jid)s" -msgstr "Petición de adición para la cuenta %s de %s" +msgstr "Petición de adición para la cuenta %(account)s de %(jid)s"
-#: ../src/dialogs.py:1617 +#: ../src/dialogs.py:1607 #, python-format msgid "Subscription request from %s" msgstr "Petición de adición de %s"
-#: ../src/dialogs.py:1678 ../src/gajim.py:2617 +#: ../src/dialogs.py:1668 ../src/gajim.py:2632 #, python-format msgid "You are already in group chat %s" msgstr "Ya estás en el salón %s"
-#: ../src/dialogs.py:1686 +#: ../src/dialogs.py:1676 msgid "You can not join a group chat unless you are connected." msgstr "No puedes entrar a un salón de chat hasta que no estés conectado."
-#: ../src/dialogs.py:1705 +#: ../src/dialogs.py:1695 #, python-format msgid "Join Group Chat with account %s" msgstr "Entrar a un grupo de charla con la cuenta %s"
-#: ../src/dialogs.py:1776 -#, fuzzy +#: ../src/dialogs.py:1766 msgid "Invalid Nickname" -msgstr "Nombre de usuario no válido" +msgstr "Alias no válido"
-#: ../src/dialogs.py:1777 ../src/groupchat_control.py:1453 -#: ../src/groupchat_control.py:1734 -#, fuzzy +#: ../src/dialogs.py:1767 ../src/groupchat_control.py:1454 +#: ../src/groupchat_control.py:1735 msgid "The nickname has not allowed characters." -msgstr "" -"La ID de Jabber para el grupo de charla tiene caracteres no permitidos." +msgstr "El alias tiene caracteres no permitidos."
-#: ../src/dialogs.py:1781 ../src/dialogs.py:1787 -#: ../src/groupchat_control.py:1901 +#: ../src/dialogs.py:1771 ../src/dialogs.py:1777 +#: ../src/groupchat_control.py:1902 msgid "Invalid group chat Jabber ID" msgstr "ID de Jabber no válida para el grupo de charla"
-#: ../src/dialogs.py:1782 ../src/dialogs.py:1788 -#: ../src/groupchat_control.py:1902 +#: ../src/dialogs.py:1772 ../src/dialogs.py:1778 +#: ../src/groupchat_control.py:1903 msgid "The group chat Jabber ID has not allowed characters." msgstr "" "La ID de Jabber para el grupo de charla tiene caracteres no permitidos."
-#: ../src/dialogs.py:1794 +#: ../src/dialogs.py:1784 msgid "This is not a group chat" msgstr "Esto no es un grupo de charla"
-#: ../src/dialogs.py:1795 +#: ../src/dialogs.py:1785 #, python-format msgid "%s is not the name of a group chat." msgstr "%s no es el nombre de un grupo de charla."
-#: ../src/dialogs.py:1822 -#, fuzzy +#: ../src/dialogs.py:1812 msgid "Without a connection, you can not synchronise your contacts." -msgstr "Sin una conexión, no puedes cambiar tu contraseña." +msgstr "Sin una conexión, no puedes sincronizar tus contactos."
-#: ../src/dialogs.py:1836 +#: ../src/dialogs.py:1826 msgid "Server" msgstr "Servidor"
-#: ../src/dialogs.py:1869 -#, fuzzy +#: ../src/dialogs.py:1859 msgid "This account is not connected to the server" -msgstr "La cuenta "%s" está conectada al servidor" +msgstr "Esta cuentano está conectada al servidor"
-#: ../src/dialogs.py:1870 -#, fuzzy +#: ../src/dialogs.py:1860 msgid "You cannot synchronize with an account unless it is connected." -msgstr "No puedes entrar a un salón de chat hasta que no estés conectado." +msgstr "No puedes sincronizar con una cuenta hasta que no te hayas conectado."
-#: ../src/dialogs.py:1894 +#: ../src/dialogs.py:1884 msgid "Synchronise" -msgstr "" +msgstr "Sincronizar"
-#: ../src/dialogs.py:1952 +#: ../src/dialogs.py:1942 #, python-format msgid "Start Chat with account %s" msgstr "Iniciar conversación con la cuenta %s"
-#: ../src/dialogs.py:1954 +#: ../src/dialogs.py:1944 msgid "Start Chat" msgstr "Iniciar conversación"
-#: ../src/dialogs.py:1955 +#: ../src/dialogs.py:1945 msgid "" "Fill in the nickname or the Jabber ID of the contact you would like\n" "to send a chat message to:" @@ -6736,315 +6601,319 @@ "enviar un mensaje de charla:"
#. if offline or connecting -#: ../src/dialogs.py:1979 ../src/dialogs.py:2353 ../src/dialogs.py:2496 +#: ../src/dialogs.py:1969 ../src/dialogs.py:2344 ../src/dialogs.py:2494 #: ../src/normal_control.py:640 msgid "Connection not available" msgstr "Conexión no disponible"
-#: ../src/dialogs.py:1980 ../src/dialogs.py:2354 ../src/dialogs.py:2497 +#: ../src/dialogs.py:1970 ../src/dialogs.py:2345 ../src/dialogs.py:2495 #: ../src/normal_control.py:641 #, python-format msgid "Please make sure you are connected with "%s"." msgstr "Por favor, asegúrate de estar conectado con "%s"."
-#: ../src/dialogs.py:1989 ../src/dialogs.py:1992 +#: ../src/dialogs.py:1979 ../src/dialogs.py:1982 msgid "Invalid JID" msgstr "ID de Jabber no válida"
-#: ../src/dialogs.py:1992 +#: ../src/dialogs.py:1982 #, python-format msgid "Unable to parse "%s"." msgstr "No se puede analizar "%s"."
-#: ../src/dialogs.py:2001 +#: ../src/dialogs.py:1991 msgid "Without a connection, you can not change your password." msgstr "Sin una conexión, no puedes cambiar tu contraseña."
-#: ../src/dialogs.py:2020 +#: ../src/dialogs.py:2010 msgid "Invalid password" msgstr "Contraseña no válida"
-#: ../src/dialogs.py:2020 +#: ../src/dialogs.py:2010 msgid "You must enter a password." msgstr "Debes introducir una contraseña."
-#: ../src/dialogs.py:2024 +#: ../src/dialogs.py:2014 msgid "Passwords do not match" msgstr "Las contraseñas no coinciden"
-#: ../src/dialogs.py:2025 +#: ../src/dialogs.py:2015 msgid "The passwords typed in both fields must be identical." msgstr "Las contraseñas escritas en ambos campos deben ser idénticas."
#. img to display #. default value -#: ../src/dialogs.py:2064 ../src/notify.py:245 ../src/notify.py:459 +#: ../src/dialogs.py:2055 ../src/notify.py:245 ../src/notify.py:473 #: ../src/osx/growler.py:12 msgid "Contact Signed In" msgstr "Contacto conectado"
-#: ../src/dialogs.py:2066 ../src/notify.py:253 ../src/notify.py:461 +#: ../src/dialogs.py:2057 ../src/notify.py:253 ../src/notify.py:475 #: ../src/osx/growler.py:12 msgid "Contact Signed Out" msgstr "Contacto desconectado"
#. chat message -#: ../src/dialogs.py:2068 ../src/notify.py:276 ../src/notify.py:463 +#: ../src/dialogs.py:2059 ../src/notify.py:276 ../src/notify.py:477 #: ../src/osx/growler.py:12 msgid "New Message" msgstr "Nuevo mensaje"
#. single message -#: ../src/dialogs.py:2068 ../src/notify.py:257 ../src/notify.py:463 +#: ../src/dialogs.py:2059 ../src/notify.py:257 ../src/notify.py:477 #: ../src/osx/growler.py:13 msgid "New Single Message" msgstr "Nuevo mensaje"
#. private message -#: ../src/dialogs.py:2069 ../src/notify.py:264 ../src/notify.py:464 +#: ../src/dialogs.py:2060 ../src/notify.py:264 ../src/notify.py:478 #: ../src/osx/growler.py:13 msgid "New Private Message" msgstr "Nuevo mensaje privado"
-#: ../src/dialogs.py:2069 ../src/gajim.py:1587 ../src/notify.py:472 +#: ../src/dialogs.py:2060 ../src/gajim.py:1594 ../src/notify.py:486 #: ../src/osx/growler.py:13 msgid "New E-mail" msgstr "Nuevo correo-e"
-#: ../src/dialogs.py:2071 ../src/gajim.py:1653 ../src/notify.py:466 +#: ../src/dialogs.py:2062 ../src/gajim.py:1660 ../src/notify.py:480 #: ../src/osx/growler.py:14 msgid "File Transfer Request" msgstr "Petición de transferencia de archivo"
-#: ../src/dialogs.py:2073 ../src/gajim.py:1553 ../src/gajim.py:1620 -#: ../src/notify.py:468 ../src/osx/growler.py:14 +#: ../src/dialogs.py:2064 ../src/gajim.py:1560 ../src/gajim.py:1627 +#: ../src/notify.py:482 ../src/osx/growler.py:14 msgid "File Transfer Error" msgstr "Error en la transferencia del archivo"
-#: ../src/dialogs.py:2075 ../src/gajim.py:1695 ../src/gajim.py:1717 -#: ../src/gajim.py:1734 ../src/notify.py:470 ../src/osx/growler.py:15 +#: ../src/dialogs.py:2066 ../src/gajim.py:1702 ../src/gajim.py:1724 +#: ../src/gajim.py:1741 ../src/notify.py:484 ../src/osx/growler.py:15 msgid "File Transfer Completed" msgstr "Transferencia del archivo finalizada"
-#: ../src/dialogs.py:2076 ../src/gajim.py:1698 ../src/notify.py:470 +#: ../src/dialogs.py:2067 ../src/gajim.py:1705 ../src/notify.py:484 #: ../src/osx/growler.py:15 msgid "File Transfer Stopped" msgstr "Transferencia del archivo detenida"
-#: ../src/dialogs.py:2078 ../src/gajim.py:1445 ../src/notify.py:474 +#: ../src/dialogs.py:2069 ../src/gajim.py:1450 ../src/notify.py:488 #: ../src/osx/growler.py:16 msgid "Groupchat Invitation" msgstr "Invitación a grupo de charla"
-#: ../src/dialogs.py:2080 ../src/notify.py:237 ../src/notify.py:476 +#: ../src/dialogs.py:2071 ../src/notify.py:237 ../src/notify.py:490 #: ../src/osx/growler.py:16 msgid "Contact Changed Status" msgstr "Contacto cambió su estado"
-#: ../src/dialogs.py:2272 ../src/normal_control.py:241 +#: ../src/dialogs.py:2263 ../src/normal_control.py:241 #, python-format msgid "Single Message using account %s" msgstr "Nuevo mensaje con la cuenta %s"
-#: ../src/dialogs.py:2274 ../src/normal_control.py:243 +#: ../src/dialogs.py:2265 ../src/normal_control.py:243 #, python-format msgid "Single Message in account %s" msgstr "Nuevo mensaje en la cuenta %s"
-#: ../src/dialogs.py:2276 ../src/normal_control.py:245 +#: ../src/dialogs.py:2267 ../src/normal_control.py:245 msgid "Single Message" msgstr "Mensaje:"
#. prepare UI for Sending -#: ../src/dialogs.py:2279 ../src/normal_control.py:248 +#: ../src/dialogs.py:2270 ../src/normal_control.py:248 #, python-format msgid "Send %s" msgstr "Enviar %s"
#. prepare UI for Receiving -#: ../src/dialogs.py:2302 ../src/normal_control.py:290 +#: ../src/dialogs.py:2293 ../src/normal_control.py:290 #, python-format msgid "Received %s" msgstr "Recibido %s"
#. prepare UI for Receiving -#: ../src/dialogs.py:2325 ../src/normal_control.py:324 -#, fuzzy, python-format +#: ../src/dialogs.py:2316 ../src/normal_control.py:324 +#, python-format msgid "Form %s" msgstr "De %s"
#. we create a new blank window to send and we preset RE: and to jid -#: ../src/dialogs.py:2397 ../src/normal_control.py:692 +#: ../src/dialogs.py:2395 ../src/normal_control.py:692 #, python-format msgid "RE: %s" msgstr "RE: %s"
-#: ../src/dialogs.py:2398 ../src/normal_control.py:696 +#: ../src/dialogs.py:2396 ../src/normal_control.py:696 #, python-format msgid "%s wrote:\n" msgstr "%s escribió:\n"
-#: ../src/dialogs.py:2442 +#: ../src/dialogs.py:2440 #, python-format msgid "XML Console for %s" msgstr "Consola XML para %s"
-#: ../src/dialogs.py:2444 +#: ../src/dialogs.py:2442 msgid "XML Console" msgstr "Consola XML"
-#: ../src/dialogs.py:2568 +#: ../src/dialogs.py:2566 #, python-format msgid "Privacy List <b><i>%s</i></b>" msgstr "Lista de privacidad <b><i>%s</i></b>"
-#: ../src/dialogs.py:2572 +#: ../src/dialogs.py:2570 #, python-format msgid "Privacy List for %s" msgstr "Lista de privacidad para %s"
-#: ../src/dialogs.py:2628 -#, fuzzy, python-format +#: ../src/dialogs.py:2626 +#, python-format msgid "Order: %(order)s, action: %(action)s, type: %(type)s, value: %(value)s" -msgstr "Orden: %s, acción: %s, tipo: %s, valor: %s" +msgstr "Orden: %(order)s, acción: %(action)s, tipo: %(type)s, valor: %(value)s"
-#: ../src/dialogs.py:2633 -#, fuzzy, python-format +#: ../src/dialogs.py:2631 +#, python-format msgid "Order: %(order)s, action: %(action)s" -msgstr "Orden: %s, acción: %s" +msgstr "Orden: %(order)s, acción: %(action)s"
-#: ../src/dialogs.py:2675 +#: ../src/dialogs.py:2673 msgid "<b>Edit a rule</b>" msgstr "<b>Editar una regla</b>"
-#: ../src/dialogs.py:2762 +#: ../src/dialogs.py:2760 msgid "<b>Add a rule</b>" msgstr "<b>Añadir una regla</b>"
-#: ../src/dialogs.py:2858 +#: ../src/dialogs.py:2856 #, python-format msgid "Privacy Lists for %s" msgstr "Listas de privacidad para %s"
-#: ../src/dialogs.py:2860 +#: ../src/dialogs.py:2858 msgid "Privacy Lists" msgstr "Listas de privacidad"
-#: ../src/dialogs.py:2930 +#: ../src/dialogs.py:2928 msgid "Invalid List Name" msgstr "Nombre de lista no válido"
-#: ../src/dialogs.py:2931 +#: ../src/dialogs.py:2929 msgid "You must enter a name to create a privacy list." msgstr "Debes introducir un nombre para crear una una lista de privacidad."
-#: ../src/dialogs.py:2963 -#, fuzzy +#: ../src/dialogs.py:2961 msgid "You are invited to a groupchat" -msgstr "Estás expulsado de este grupo de charla." +msgstr "Has sido invitado a un grupo de charla"
-#: ../src/dialogs.py:2966 -#, fuzzy +#: ../src/dialogs.py:2964 msgid "$Contact has invited you to join a discussion" -msgstr "%(contact_jid)s te ha invitado al grupo de charla %(room_jid)s." +msgstr "$Contact te ha invitado a entrar en una discusión"
-#: ../src/dialogs.py:2968 -#, fuzzy, python-format +#: ../src/dialogs.py:2966 +#, python-format msgid "$Contact has invited you to group chat %(room_jid)s" -msgstr "%(contact_jid)s te ha invitado al grupo de charla %(room_jid)s." +msgstr "$Contact te ha invitado al grupo de charla %(room_jid)s"
-#: ../src/dialogs.py:2976 +#: ../src/dialogs.py:2974 #, python-format msgid "Comment: %s" msgstr "Comentario: %s"
-#: ../src/dialogs.py:2978 +#: ../src/dialogs.py:2976 msgid "Do you want to accept the invitation?" -msgstr "" +msgstr "¿Quieres aceptar la invitación?"
-#: ../src/dialogs.py:3033 +#: ../src/dialogs.py:3031 msgid "Choose Sound" msgstr "Elegir sonido"
-#: ../src/dialogs.py:3043 ../src/dialogs.py:3094 +#: ../src/dialogs.py:3041 ../src/dialogs.py:3092 msgid "All files" msgstr "Todos los archivos"
-#: ../src/dialogs.py:3048 +#: ../src/dialogs.py:3046 msgid "Wav Sounds" msgstr "Sonidos Wav"
-#: ../src/dialogs.py:3081 +#: ../src/dialogs.py:3079 msgid "Choose Image" msgstr "Elegir Imagen"
-#: ../src/dialogs.py:3099 +#: ../src/dialogs.py:3097 msgid "Images" msgstr "Imágenes"
-#: ../src/dialogs.py:3164 +#: ../src/dialogs.py:3162 #, python-format msgid "When %s becomes:" msgstr "Cuando %s esté:"
-#: ../src/dialogs.py:3166 +#: ../src/dialogs.py:3164 #, python-format msgid "Adding Special Notification for %s" msgstr "Añadiendo notificación especial para %s"
#. # means number -#: ../src/dialogs.py:3237 +#: ../src/dialogs.py:3233 msgid "#" msgstr ""
-#: ../src/dialogs.py:3243 +#: ../src/dialogs.py:3239 msgid "Condition" msgstr "Condición"
-#: ../src/dialogs.py:3361 +#: ../src/dialogs.py:3357 msgid "when I am " msgstr "cuando esté "
-#: ../src/dialogs.py:3836 +#: ../src/dialogs.py:3829 #, python-format msgid "" "Your chat session with <b>%(jid)s</b> is encrypted.\n" "\n" "This session's Short Authentication String is <b>%(sas)s</b>." msgstr "" +"Tu conversación <b>%(jid)s</b> está cifrada.\n" +"\n" +"La cadena corta de autentificación de esta sesión es <b>%(sas)s</b>."
-#: ../src/dialogs.py:3840 +#: ../src/dialogs.py:3833 msgid "You have already verified this contact's identity." -msgstr "" +msgstr "Ya verificaste la identidad de este contacto."
-#: ../src/dialogs.py:3846 ../src/dialogs.py:3933 +#: ../src/dialogs.py:3839 ../src/dialogs.py:3926 msgid "Contact's identity verified" -msgstr "" +msgstr "Identidad del contacto verificada"
-#: ../src/dialogs.py:3854 +#: ../src/dialogs.py:3847 msgid "Verify again..." -msgstr "" +msgstr "Verificar de nuevo..."
-#: ../src/dialogs.py:3859 +#: ../src/dialogs.py:3852 msgid "" "To be certain that <b>only</b> the expected person can read your messages or " "send you messages, you need to verify their identity by clicking the button " "below." msgstr "" +"Para asegurar que <b>sólo</b> puede leer tus mensajes o enviarte mensajes la " +"persona esperada, necesitas verificar su identidad haciendo click en el " +"botón inferior."
-#: ../src/dialogs.py:3862 ../src/dialogs.py:3914 ../src/dialogs.py:3927 +#: ../src/dialogs.py:3855 ../src/dialogs.py:3907 ../src/dialogs.py:3920 msgid "Contact's identity NOT verified" -msgstr "" +msgstr "La identidad del contacto NO fue verificada"
-#: ../src/dialogs.py:3869 +#: ../src/dialogs.py:3862 msgid "Verify..." -msgstr "" +msgstr "Verificar..."
-#: ../src/dialogs.py:3881 +#: ../src/dialogs.py:3874 msgid "Have you verified the contact's identity?" -msgstr "" +msgstr "¿Has verificado la identidad del contacto?"
-#: ../src/dialogs.py:3882 +#: ../src/dialogs.py:3875 #, python-format msgid "" "To prevent talking to an unknown person, you should speak to <b>%(jid)s</b> " @@ -7053,31 +6922,43 @@ "\n" "This session's Short Authentication String is <b>%(sas)s</b>." msgstr "" +"Para prevenir hablar a una persona desconocida, debes hablar a <b>%(jid)s</" +"b> directamente (en persona o por teléfono) y verificar que ellos usan la " +"misma Cadena Corta de Autentificación (CCA) que tú.\n" +"\n" +"La Cadena Corta de Autentificación (CCA) de esta sesión es <b>%(sas)s</b>."
-#: ../src/dialogs.py:3883 +#: ../src/dialogs.py:3876 msgid "Did you talk to the remote contact and verify the SAS?" -msgstr "" +msgstr "¿Se lo dijiste a tu contacto remoto y verificaste el CCA?"
-#: ../src/dialogs.py:3915 +#: ../src/dialogs.py:3908 #, python-format msgid "The contact's key (%s) <b>does not match</b> the key assigned in Gajim." msgstr "" +"La clave del contacto (%s) <b>no coincide</b> con la clave asignada en Gajim."
-#: ../src/dialogs.py:3921 +#: ../src/dialogs.py:3914 msgid "No GPG key is assigned to this contact. So you cannot encrypt messages." msgstr "" +"No se ha asignado una clave GPG a este contacto. No podrás cifrar mensajes."
-#: ../src/dialogs.py:3928 +#: ../src/dialogs.py:3921 msgid "" "GPG key is assigned to this contact, but <b>you do not trust his key</b>, so " "message <b>cannot</b> be encrypted. Use your GPG client to trust this key." msgstr "" +"La clave GPG se asignó a este contacto, pero <b>no confías en esta clave</b> " +"de modo que <b>no</b> será cifrado. Usa tu cliente GPG para otorgar " +"confianza a esta clave."
-#: ../src/dialogs.py:3934 +#: ../src/dialogs.py:3927 msgid "" "GPG Key is assigned to this contact, and you trust his key, so messages will " "be encrypted." msgstr "" +"La clave GPG se asignó a este contacto, y confías en esta clave, de modo que " +"los mensajes serán cifrados."
#: ../src/disco.py:116 msgid "Others" @@ -7113,7 +6994,7 @@ "No hay servicio en la dirección introducida, o no está respondiendo. " "Comprueba la dirección e inténtalo de nuevo."
-#: ../src/disco.py:661 ../src/disco.py:955 +#: ../src/disco.py:661 ../src/disco.py:957 msgid "The service is not browsable" msgstr "El servicio no es navegable"
@@ -7122,36 +7003,34 @@ msgstr "Este tipo de servicio no contiene ningún elemento a navegar."
#: ../src/disco.py:699 ../src/disco.py:709 -#, fuzzy msgid "Invalid Server Name" -msgstr "Nombre de usuario no válido" +msgstr "Nombre de servidor no válido"
-#: ../src/disco.py:754 -#, fuzzy, python-format +#: ../src/disco.py:756 +#, python-format msgid "Browsing %(address)s using account %(account)s" -msgstr "Navegar %s usando la cuenta %s" +msgstr "Navegar por %(address)s usando la cuenta %(account)s"
-#: ../src/disco.py:794 +#: ../src/disco.py:796 msgid "_Browse" msgstr "_Navegar"
-#: ../src/disco.py:956 +#: ../src/disco.py:958 msgid "This service does not contain any items to browse." msgstr "Este servicio no contiene elementos para navegar."
-#: ../src/disco.py:1179 -#, fuzzy +#: ../src/disco.py:1180 msgid "_Execute Command" -msgstr "_Ejecutar comando..." +msgstr "_Ejecutar comando"
-#: ../src/disco.py:1189 ../src/disco.py:1353 +#: ../src/disco.py:1190 ../src/disco.py:1354 msgid "Re_gister" msgstr "_Suscribir"
-#: ../src/disco.py:1390 -#, fuzzy, python-format +#: ../src/disco.py:1391 +#, python-format msgid "Scanning %(current)d / %(total)d.." -msgstr "Escaneando %d / %d.." +msgstr "Escaneando %(current)d / %(total)d.."
#. Users column #: ../src/disco.py:1572 @@ -7166,267 +7045,279 @@ #. Id column #: ../src/disco.py:1588 msgid "Id" -msgstr "" +msgstr "Id"
-#: ../src/disco.py:1818 +#: ../src/disco.py:1817 msgid "Subscribed" msgstr "Añadido"
-#: ../src/disco.py:1826 -#, fuzzy +#: ../src/disco.py:1825 msgid "Node" -msgstr "Ninguno" +msgstr "Nodo"
-#: ../src/disco.py:1883 +#: ../src/disco.py:1882 msgid "New post" msgstr "Nuevo post"
-#: ../src/disco.py:1889 +#: ../src/disco.py:1888 msgid "_Subscribe" msgstr "_Añadir"
-#: ../src/disco.py:1895 +#: ../src/disco.py:1894 msgid "_Unsubscribe" msgstr "_Eliminar"
-#: ../src/features_window.py:51 +#: ../src/features_window.py:49 msgid "PyOpenSSL" -msgstr "" +msgstr "PyOpenSSL"
-#: ../src/features_window.py:52 +#: ../src/features_window.py:50 msgid "" "A library used to validate server certificates to ensure a secure connection." msgstr "" +"Una librería usada para validar certificados de servidor para asegurar " +"conexiones seguras."
-#: ../src/features_window.py:53 ../src/features_window.py:54 +#: ../src/features_window.py:51 ../src/features_window.py:52 msgid "Requires python-pyopenssl." -msgstr "" +msgstr "Requiere python-pyopenssl."
-#: ../src/features_window.py:55 +#: ../src/features_window.py:53 msgid "Bonjour / Zeroconf" msgstr ""
-#: ../src/features_window.py:56 +#: ../src/features_window.py:54 msgid "Serverless chatting with autodetected clients in a local network." -msgstr "" +msgstr "Charla sin servidor con clientes autodetectados en una red local."
-#: ../src/features_window.py:57 +#: ../src/features_window.py:55 msgid "Requires python-avahi." -msgstr "" +msgstr "Requiere python-avahi."
-#: ../src/features_window.py:58 +#: ../src/features_window.py:56 msgid "Requires pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour)." -msgstr "" +msgstr "Requiere pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour)."
-#: ../src/features_window.py:59 +#: ../src/features_window.py:57 msgid "gajim-remote" msgstr ""
-#: ../src/features_window.py:60 +#: ../src/features_window.py:58 msgid "A script to control Gajim via commandline." -msgstr "" +msgstr "Un script para controlar Gajim vía línea de comandos."
-#: ../src/features_window.py:61 +#: ../src/features_window.py:59 msgid "Requires python-dbus." -msgstr "" +msgstr "Requiere python-dbus."
-#: ../src/features_window.py:62 ../src/features_window.py:66 -#: ../src/features_window.py:70 ../src/features_window.py:74 -#: ../src/features_window.py:78 ../src/features_window.py:86 -#: ../src/features_window.py:90 +#: ../src/features_window.py:60 ../src/features_window.py:64 +#: ../src/features_window.py:68 ../src/features_window.py:72 +#: ../src/features_window.py:76 ../src/features_window.py:84 +#: ../src/features_window.py:88 msgid "Feature not available under Windows." -msgstr "" +msgstr "Característica no disponible en Windows."
-#: ../src/features_window.py:63 -#, fuzzy +#: ../src/features_window.py:61 msgid "OpenGPG" -msgstr "OpenPGP: " +msgstr "OpenGPG"
-#: ../src/features_window.py:64 +#: ../src/features_window.py:62 msgid "Encrypting chatmessages with gpg keys." -msgstr "" +msgstr "Cifrado de conversaciones con claves gpg"
-#: ../src/features_window.py:65 +#: ../src/features_window.py:63 msgid "Requires gpg and python-GnuPGInterface." -msgstr "" +msgstr "Requiere gpg y python-GnuPGInterface."
-#: ../src/features_window.py:67 -#, fuzzy +#: ../src/features_window.py:65 msgid "network-manager" -msgstr "Histórico" +msgstr ""
-#: ../src/features_window.py:68 +#: ../src/features_window.py:66 msgid "Autodetection of network status." -msgstr "" +msgstr "Autodetección del estado de la red."
-#: ../src/features_window.py:69 +#: ../src/features_window.py:67 msgid "Requires gnome-network-manager and python-dbus." -msgstr "" +msgstr "Requiere gnome-network-manager y python-dbus."
-#: ../src/features_window.py:71 -#, fuzzy +#: ../src/features_window.py:69 msgid "Session Management" -msgstr "Mensaje enviado" +msgstr "Gestión de sesiones"
-#: ../src/features_window.py:72 +#: ../src/features_window.py:70 msgid "Gajim session is stored on logout and restored on login." -msgstr "" +msgstr "La sesión de Gajim se guarda al desconectar y se restaura al conectar."
-#: ../src/features_window.py:73 +#: ../src/features_window.py:71 msgid "Requires python-gnome2." -msgstr "" +msgstr "Requiere python-gnome2."
-#: ../src/features_window.py:75 +#: ../src/features_window.py:73 msgid "gnome-keyring" msgstr ""
-#: ../src/features_window.py:76 +#: ../src/features_window.py:74 msgid "Passwords can be stored securely and not just in plaintext." msgstr "" +"Las contraseñas se pueden guardar de forma segura y no sólo en texto plano."
-#: ../src/features_window.py:77 +#: ../src/features_window.py:75 msgid "Requires gnome-keyring and python-gnome2-desktop." -msgstr "" +msgstr "Requiere gnome-keyring y python-gnome2-desktop."
-#: ../src/features_window.py:79 +#: ../src/features_window.py:77 msgid "SRV" msgstr ""
-#: ../src/features_window.py:80 +#: ../src/features_window.py:78 msgid "Ability to connect to servers which are using SRV records." -msgstr "" +msgstr "Posibilidad de conectar a servidores que usen registros SRV"
-#: ../src/features_window.py:81 +#: ../src/features_window.py:79 msgid "Requires dnsutils." -msgstr "" +msgstr "Requiere dnsutils"
-#: ../src/features_window.py:82 +#: ../src/features_window.py:80 msgid "Requires nslookup to use SRV records." -msgstr "" +msgstr "Requiere nslookup para usar los registros SRV."
-#: ../src/features_window.py:83 +#: ../src/features_window.py:81 msgid "Spell Checker" -msgstr "" +msgstr "Corrector ortográfico"
-#: ../src/features_window.py:84 +#: ../src/features_window.py:82 msgid "Spellchecking of composed messages." -msgstr "" +msgstr "Corrección ortográfica de los mensjes compuestos."
-#: ../src/features_window.py:85 +#: ../src/features_window.py:83 msgid "" "Requires python-gnome2-extras or compilation of gtkspell module from Gajim " "sources." msgstr "" +"Requiere python-gnome2-extras o la compilación del módulo gtkspell de las " +"fuentes de Gajim."
-#: ../src/features_window.py:87 +#: ../src/features_window.py:85 msgid "Notification-daemon" -msgstr "" +msgstr "Demonio notificador"
-#: ../src/features_window.py:88 +#: ../src/features_window.py:86 msgid "Passive popups notifying for new events." -msgstr "" +msgstr "Mensajes emergentes pasivas para nuevos eventos."
-#: ../src/features_window.py:89 +#: ../src/features_window.py:87 msgid "" "Requires python-notify or instead python-dbus in conjunction with " "notification-daemon." msgstr "" +"Requiere python-notify o python-dbus en conjunción con notification-daemon " +"en su lugar-"
-#: ../src/features_window.py:91 +#: ../src/features_window.py:89 msgid "Trayicon" -msgstr "" +msgstr "Icono del área de notificación"
-#: ../src/features_window.py:92 +#: ../src/features_window.py:90 msgid "A icon in systemtray reflecting the current presence." -msgstr "" +msgstr "Un icono en el área de notificación reflejando la presencia actual."
-#: ../src/features_window.py:93 +#: ../src/features_window.py:91 msgid "" "Requires python-gnome2-extras or compiled trayicon module from Gajim sources." msgstr "" +"Requiere python-gnome2-extras o el módulo trayicon compilado de las fuentes " +"de Gajim."
-#: ../src/features_window.py:94 +#: ../src/features_window.py:92 msgid "Requires PyGTK >= 2.10." -msgstr "" +msgstr "Requiere PyGTK >= 2.10."
-#: ../src/features_window.py:95 -#, fuzzy +#: ../src/features_window.py:93 msgid "Idle" -msgstr "alemán" +msgstr "Inactivo"
-#: ../src/features_window.py:96 +#: ../src/features_window.py:94 msgid "Ability to measure idle time, in order to set auto status." msgstr "" +"Posibilidad de medir el tiempo de inactividad para definir el auto-estado."
-#: ../src/features_window.py:97 ../src/features_window.py:98 -msgid "Requires compilation of the idle module from Gajim sources." +#: ../src/features_window.py:95 +msgid "Requires libxss library." msgstr ""
-#: ../src/features_window.py:99 +#: ../src/features_window.py:96 +#, fuzzy +msgid "Requires python2.5." +msgstr "Requiere python-gnome2." + +#: ../src/features_window.py:97 msgid "LaTeX" -msgstr "" +msgstr "LaTeX"
-#: ../src/features_window.py:100 +#: ../src/features_window.py:98 msgid "Transform LaTeX expressions between $$ $$." -msgstr "" +msgstr "Transformar expresiones LaTeX entre $$ $$."
-#: ../src/features_window.py:101 +#: ../src/features_window.py:99 msgid "" "Requires texlive-latex-base and dvipng. You have to set 'use_latex' to True " "in the Advanced Configuration Editor." msgstr "" +"Requiere texlive-latex-base y dvipng. Tienes que definir 'use_latex' a True " +"en el Editor de Configuración Avanzada."
-#: ../src/features_window.py:102 +#: ../src/features_window.py:100 msgid "" "Requires texlive-latex-base and dvipng (All is in MikTeX). You have to set " "'use_latex' to True in the Advanced Configuration Editor." msgstr "" +"Requiere textlive-latex-base y dvipng (Todo está en MikTeX). Tienes que " +"definir 'use_latex' a True en el Editor de Configuración Avanzada."
-#: ../src/features_window.py:103 -#, fuzzy +#: ../src/features_window.py:101 msgid "End to End Encryption" -msgstr "Encriptación OpenPGP" +msgstr "Cifrado End to End"
-#: ../src/features_window.py:104 -#, fuzzy +#: ../src/features_window.py:102 msgid "Encrypting chatmessages." -msgstr "Mens_aje entrante:" +msgstr "Cifrando conversaciones."
-#: ../src/features_window.py:105 ../src/features_window.py:106 +#: ../src/features_window.py:103 ../src/features_window.py:104 msgid "Requires python-crypto." -msgstr "" +msgstr "Requiere python-crypto"
-#: ../src/features_window.py:107 -#, fuzzy +#: ../src/features_window.py:105 msgid "RST Generator" -msgstr "General" +msgstr "Generador RST"
-#: ../src/features_window.py:108 +#: ../src/features_window.py:106 msgid "" "Generate XHTML output from RST code (see http://docutils.sourceforge.net/" "docs/ref/rst/restructuredtext.html)." msgstr "" +"Generar salida XHTML partiendo de un código RST (mira http://docutils." +"sourceforge.net/docs/ref/rst/restructuredtext.html)."
-#: ../src/features_window.py:109 ../src/features_window.py:110 +#: ../src/features_window.py:107 ../src/features_window.py:108 msgid "Requires python-docutils." -msgstr "" +msgstr "Requiere python-docutils"
-#: ../src/features_window.py:111 +#: ../src/features_window.py:109 msgid "libsexy" -msgstr "" +msgstr "libsexy"
-#: ../src/features_window.py:112 +#: ../src/features_window.py:110 msgid "Ability to have clickable URLs in chat and groupchat window banners." -msgstr "" +msgstr "Posibilidad de hacer URLs clickables en las ventanas de charla"
-#: ../src/features_window.py:113 ../src/features_window.py:114 +#: ../src/features_window.py:111 ../src/features_window.py:112 msgid "Requires python-sexy." -msgstr "" +msgstr "Requiere python-sexy"
-#: ../src/features_window.py:128 -#, fuzzy +#: ../src/features_window.py:126 msgid "Feature" -msgstr "Características de los servidores" +msgstr "Característica"
#: ../src/filetransfers_window.py:76 msgid "File" @@ -7453,7 +7344,7 @@ #. You is a reply of who sent a file #. You is a reply of who received a file #: ../src/filetransfers_window.py:183 ../src/filetransfers_window.py:193 -#: ../src/history_manager.py:499 +#: ../src/history_manager.py:496 msgid "You" msgstr "Tú"
@@ -7477,7 +7368,6 @@ msgstr "Transferencia de archivo finalizada"
#: ../src/filetransfers_window.py:212 ../src/filetransfers_window.py:218 -#, fuzzy msgid "File transfer cancelled" msgstr "Transferencia de archivo cancelada"
@@ -7496,7 +7386,6 @@ msgstr "Mensaje de error: %s"
#: ../src/filetransfers_window.py:231 -#, fuzzy msgid "File transfer stopped by the contact at the other end" msgstr "Transferencia de archivo detenida por el contacto de la otra parte"
@@ -7505,9 +7394,8 @@ msgstr "Elige archivo a enviar..."
#: ../src/filetransfers_window.py:268 ../src/tooltips.py:709 -#, fuzzy msgid "Description: " -msgstr "Descripción: %s" +msgstr "Descripción: "
#: ../src/filetransfers_window.py:279 msgid "Gajim cannot access this file" @@ -7537,12 +7425,12 @@ msgid "%s wants to send you a file:" msgstr "%s quiere enviarte un archivo:"
-#: ../src/filetransfers_window.py:332 ../src/gtkgui_helpers.py:821 +#: ../src/filetransfers_window.py:332 ../src/gtkgui_helpers.py:817 #, python-format msgid "Cannot overwrite existing file "%s"" msgstr "No se puede sobreescribir el archivo existente "%s""
-#: ../src/filetransfers_window.py:333 ../src/gtkgui_helpers.py:823 +#: ../src/filetransfers_window.py:333 ../src/gtkgui_helpers.py:819 msgid "" "A file with this name already exists and you do not have permission to " "overwrite it." @@ -7550,22 +7438,22 @@ "Un archivo con este nombre ya existe y no tienes permisos para " "sobreescribirlo"
-#: ../src/filetransfers_window.py:349 ../src/gtkgui_helpers.py:827 +#: ../src/filetransfers_window.py:349 ../src/gtkgui_helpers.py:823 msgid "This file already exists" msgstr "Este archivo ya existe"
-#: ../src/filetransfers_window.py:349 ../src/gtkgui_helpers.py:827 +#: ../src/filetransfers_window.py:349 ../src/gtkgui_helpers.py:823 msgid "What do you want to do?" msgstr "¿Qué quieres hacer?"
#. read-only bit is used to mark special folder under windows, #. not to mark that a folder is read-only. See ticket #3587 -#: ../src/filetransfers_window.py:359 ../src/gtkgui_helpers.py:834 +#: ../src/filetransfers_window.py:359 ../src/gtkgui_helpers.py:830 #, python-format msgid "Directory "%s" is not writable" msgstr "No se puede escribir en el directorio "%s""
-#: ../src/filetransfers_window.py:359 ../src/gtkgui_helpers.py:835 +#: ../src/filetransfers_window.py:359 ../src/gtkgui_helpers.py:831 msgid "You do not have permission to create files in this directory." msgstr "No tienes permisos para crear archivos en este directorio."
@@ -7579,7 +7467,7 @@ #: ../src/filetransfers_window.py:435 #, python-format msgid "%(hours)02.d:%(minutes)02.d:%(seconds)02.d" -msgstr "" +msgstr "%(hours)02.d:%(minutes)02.d:%(seconds)02.d"
#. This should make the string Kb/s, #. where 'Kb' part is taken from %s. @@ -7587,7 +7475,7 @@ #: ../src/filetransfers_window.py:526 #, python-format msgid "(%(filesize_unit)s/s)" -msgstr "" +msgstr "(%(filesize_unit)s/s)"
#: ../src/filetransfers_window.py:566 ../src/filetransfers_window.py:569 msgid "Invalid File" @@ -7609,45 +7497,40 @@ msgid "Sender: " msgstr "Remitente: "
-#: ../src/filetransfers_window.py:782 +#: ../src/filetransfers_window.py:781 msgid "Pause" msgstr "Pausa"
-#: ../src/gajim.py:83 +#: ../src/gajim.py:93 #, python-format msgid "%s is not a valid loglevel" -msgstr "" +msgstr "%s no es un nivel de registro válido"
-#: ../src/gajim.py:156 -#, fuzzy +#: ../src/gajim.py:165 msgid "Gajim needs X server to run. Quiting..." -msgstr "Gajim necesita Xserver para funcionar. Abortando..." +msgstr "Gajim necesita el servidor X para para funcionar. Abortando..."
-#: ../src/gajim.py:186 -#, fuzzy +#: ../src/gajim.py:194 msgid "Gajim needs PyGTK 2.8 or above" -msgstr "Gajim necesita PyGTK 2.6 o superior" +msgstr "Gajim necesita PyGTK 2.8 o superior"
-#: ../src/gajim.py:187 -#, fuzzy +#: ../src/gajim.py:195 msgid "Gajim needs PyGTK 2.8 or above to run. Quiting..." -msgstr "Gajim necesita PyGTK 2.6 o superior para funcionar. Abortando..." +msgstr "Gajim necesita PyGTK 2.8 o superior para funcionar. Abortando..."
-#: ../src/gajim.py:189 -#, fuzzy +#: ../src/gajim.py:197 msgid "Gajim needs GTK 2.8 or above" -msgstr "Gajim necesita GTK 2.6 o superior" +msgstr "Gajim necesita GTK 2.8 o superior"
-#: ../src/gajim.py:190 -#, fuzzy +#: ../src/gajim.py:198 msgid "Gajim needs GTK 2.8 or above to run. Quiting..." -msgstr "Gajim necesita GTK 2.6 o superior para funcionar. Abortando..." +msgstr "Gajim necesita GTK 2.8 o superior para funcionar. Abortando..."
-#: ../src/gajim.py:195 +#: ../src/gajim.py:203 msgid "GTK+ runtime is missing libglade support" msgstr "La biblioteca GTK+ necesita soporte de libglade"
-#: ../src/gajim.py:197 +#: ../src/gajim.py:205 #, python-format msgid "" "Please remove your current GTK+ runtime and install the latest stable " @@ -7656,22 +7539,22 @@ "Por favor elimina tu actual biblioteca GTK+ e instala la última versión " "estable desde %s"
-#: ../src/gajim.py:199 +#: ../src/gajim.py:207 msgid "" "Please make sure that GTK+ and PyGTK have libglade support in your system." msgstr "" "Por favor, asegúrate de que GTK+ y PyGTK tienen soporte libglade en tu " "sistema."
-#: ../src/gajim.py:204 +#: ../src/gajim.py:212 msgid "Gajim needs PySQLite2 to run" msgstr "Gajim necesita PySQLite2 para funcionar"
-#: ../src/gajim.py:212 +#: ../src/gajim.py:220 msgid "Gajim needs pywin32 to run" msgstr "Gajim necesita pywin32 para funcionar"
-#: ../src/gajim.py:213 +#: ../src/gajim.py:221 #, python-format msgid "" "Please make sure that Pywin32 is installed on your system. You can get it at " @@ -7681,11 +7564,11 @@ "obtenerlo en %s"
#. set the icon to all newly opened wind -#: ../src/gajim.py:366 +#: ../src/gajim.py:374 msgid "Gajim is already running" msgstr "Gajim ya se encuentra en ejecución"
-#: ../src/gajim.py:367 +#: ../src/gajim.py:375 msgid "" "Another instance of Gajim seems to be running\n" "Run anyway?" @@ -7693,175 +7576,181 @@ "Otra instancia de Gajim parece estar ya en ejecución\n" "¿Ejecutar otra?"
-#: ../src/gajim.py:493 +#: ../src/gajim.py:500 msgid "Passphrase Required" msgstr "Contraseña requerida"
-#: ../src/gajim.py:494 -#, fuzzy, python-format +#: ../src/gajim.py:501 +#, python-format msgid "Enter GPG key passphrase for key %(keyid)s (account %(account)s)." -msgstr "Introduce contraseña GPG para la cuenta %s" +msgstr "" +"Introduce la contraseña GPG para la clave %(keyid)s (cuenta %(account)s)."
#. ask again -#: ../src/gajim.py:509 +#: ../src/gajim.py:516 msgid "Wrong Passphrase" msgstr "Contraseña equivocada"
-#: ../src/gajim.py:510 +#: ../src/gajim.py:517 msgid "Please retype your GPG passphrase or press Cancel." msgstr "Por favor, vuelve a escribir tu contraseña GPG o presiona Cancelar."
-#: ../src/gajim.py:557 -#, fuzzy, python-format +#: ../src/gajim.py:564 +#, 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 "" -"Tu alias deseado está en uso o registrado por otro ocupante. \n" -"Por favor, especifica otro alias abajo:" +"Tu alias deseado en el grupo de charla %s está en uso o registrado por otro " +"ocupante.\n" +"Especifica otro alias abajo:"
-#: ../src/gajim.py:571 +#: ../src/gajim.py:578 msgid "Do you accept this request?" msgstr "¿Aceptas esta petición?"
-#: ../src/gajim.py:573 -#, fuzzy, python-format +#: ../src/gajim.py:580 +#, python-format msgid "Do you accept this request on account %s?" -msgstr "¿Aceptas esta petición?" +msgstr "¿Aceptas esta petición en la cuenta %s?"
-#: ../src/gajim.py:576 -#, fuzzy, python-format +#: ../src/gajim.py:583 +#, python-format msgid "HTTP (%(method)s) Authorization for %(url)s (id: %(id)s)" -msgstr "Autorización HTTP (%s) para %s (id: %s)" +msgstr "Autorización HTTP (%(method)s) para %(url)s (id: %(id)s)"
-#: ../src/gajim.py:624 ../src/notify.py:478 ../src/osx/growler.py:17 +#: ../src/gajim.py:631 ../src/notify.py:492 ../src/osx/growler.py:17 msgid "Connection Failed" msgstr "Falló la conexión"
-#: ../src/gajim.py:930 ../src/gajim.py:934 -#, fuzzy, python-format +#: ../src/gajim.py:934 ../src/gajim.py:938 +#, python-format msgid "Error %(code)s: %(msg)s" -msgstr "Mensaje de error: %s" +msgstr "Error %(code)s: %(msg)s"
#. ('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session)) -#: ../src/gajim.py:944 ../src/gajim.py:957 -#, fuzzy, python-format +#: ../src/gajim.py:948 ../src/gajim.py:962 +#, python-format msgid "error while sending %(message)s ( %(error)s )" -msgstr "error mientras se enviaba %s ( %s )" +msgstr "error mientras se enviaba %(message)s ( %(error)s )"
-#: ../src/gajim.py:989 +#: ../src/gajim.py:994 msgid "Authorization accepted" msgstr "Autorización aceptada"
-#: ../src/gajim.py:990 +#: ../src/gajim.py:995 #, python-format msgid "The contact "%s" has authorized you to see his or her status." msgstr "El contacto "%s" te ha autorizado a ver su estado."
-#: ../src/gajim.py:1009 +#: ../src/gajim.py:1014 #, python-format msgid "Contact "%s" removed subscription from you" msgstr "El contacto "%s" eliminó su subscripción de tí"
-#: ../src/gajim.py:1010 +#: ../src/gajim.py:1015 msgid "" "You will always see him or her as offline.\n" "Do you want to remove him or her from your contact list?" msgstr "" +"Siempre le verás desconectado.\n" +"¿Le quieres eliminar de tu lista de contactos?"
-#: ../src/gajim.py:1051 +#: ../src/gajim.py:1056 #, python-format msgid "Contact with "%s" cannot be established" msgstr "No se ha podido establecer contacto con "%s""
-#: ../src/gajim.py:1227 ../src/groupchat_control.py:1164 -#, fuzzy, python-format +#: ../src/gajim.py:1232 ../src/groupchat_control.py:1165 +#, python-format msgid "%(nick)s is now known as %(new_nick)s" -msgstr "%s es ahora conocido como %s" +msgstr "%(nick)s ahora es conocido como %(new_nick)s"
-#: ../src/gajim.py:1245 ../src/groupchat_control.py:1339 -#: ../src/history_window.py:417 ../src/notify.py:232 +#: ../src/gajim.py:1250 ../src/groupchat_control.py:1340 +#: ../src/history_window.py:428 ../src/notify.py:232 #, python-format msgid "%(nick)s is now %(status)s" msgstr "%(nick)s está ahora %(status)s"
-#: ../src/gajim.py:1308 +#: ../src/gajim.py:1313 #, python-format msgid "%(jid)s has set the subject to %(subject)s" -msgstr "" +msgstr "%(jid)s ha cambiado el asunto a %(subject)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:1372 ../src/groupchat_control.py:1124 +#: ../src/gajim.py:1377 ../src/groupchat_control.py:1125 msgid "Any occupant is allowed to see your full JID" -msgstr "" +msgstr "Cualquier ocupante puede ver tu JID completo"
-#: ../src/gajim.py:1375 +#: ../src/gajim.py:1380 msgid "Room now shows unavailable member" -msgstr "" +msgstr "El salón ahora muestra miembros no disponibles"
-#: ../src/gajim.py:1377 +#: ../src/gajim.py:1382 msgid "room now does not show unavailable members" -msgstr "" +msgstr "el salón ahora no muestra miembros no disponibles"
-#: ../src/gajim.py:1380 +#: ../src/gajim.py:1385 msgid "A non-privacy-related room configuration change has occurred" -msgstr "" +msgstr "Ha ocurrido un cambio en el salón no relacionado con la privacidad"
#. Can be a presence (see chg_contact_status in groupchat_control.py) -#: ../src/gajim.py:1383 +#: ../src/gajim.py:1388 msgid "Room logging is now enabled" -msgstr "" +msgstr "Ha sido activado el registro del salón"
-#: ../src/gajim.py:1385 +#: ../src/gajim.py:1390 msgid "Room logging is now disabled" -msgstr "" +msgstr "El salón está ahora deshabilitado"
-#: ../src/gajim.py:1387 +#: ../src/gajim.py:1392 msgid "Room is now non-anonymous" -msgstr "" +msgstr "El salón es ahora no-anónimo"
-#: ../src/gajim.py:1390 +#: ../src/gajim.py:1395 msgid "Room is now semi-anonymous" -msgstr "" +msgstr "El salón es ahora semi-anónimo"
-#: ../src/gajim.py:1393 +#: ../src/gajim.py:1398 msgid "Room is now fully-anonymous" -msgstr "" +msgstr "El salón es ahora completamente anónimo"
-#: ../src/gajim.py:1425 -#, fuzzy, python-format +#: ../src/gajim.py:1430 +#, python-format msgid "A Password is required to join the room %s. Please type it." -msgstr "Se requiere una contraseña para entrar a este grupo de charla." +msgstr "Se requiere una contraseña para entrar al salón %s. Introdúcela."
-#: ../src/gajim.py:1459 +#: ../src/gajim.py:1464 msgid "" "You configured Gajim to use GPG agent, but there is no GPG agent running or " "it returned a wrong passphrase.\n" msgstr "" +"Configuraste Gajim para usar un agente GPG, pero no hay ningún agente en " +"ejecución o el mismo devolvió una contraseña errónea.\n"
-#: ../src/gajim.py:1461 +#: ../src/gajim.py:1466 msgid "You are currently connected without your OpenPGP key." msgstr "Estás actualmente conectado sin tu clave GPG"
-#: ../src/gajim.py:1464 +#: ../src/gajim.py:1469 msgid "Your passphrase is incorrect" msgstr "Contraseña incorrecta"
-#: ../src/gajim.py:1564 +#: ../src/gajim.py:1571 #, python-format msgid "New mail on %(gmail_mail_address)s" msgstr "Correo nuevo en %(gmail_mail_address)s"
-#: ../src/gajim.py:1566 +#: ../src/gajim.py:1573 #, python-format msgid "You have %d new mail conversation" msgid_plural "You have %d new mail conversations" msgstr[0] "Tienes %d nueva conversación de correo" msgstr[1] "Tienes %d nuevas conversaciones de correo"
-#: ../src/gajim.py:1579 +#: ../src/gajim.py:1586 #, python-format msgid "" "\n" @@ -7870,100 +7759,109 @@ "Subject: %(subject)s\n" "%(snippet)s" msgstr "" +"\n" +"\n" +"De: %(from_address)s\n" +"Asunto: %(subject)s\n" +"%(snippet)s"
-#: ../src/gajim.py:1650 +#: ../src/gajim.py:1657 #, python-format msgid "%s wants to send you a file." msgstr "%s quiere enviarte un archivo."
-#: ../src/gajim.py:1718 +#: ../src/gajim.py:1725 #, python-format msgid "You successfully received %(filename)s from %(name)s." msgstr "Has recibido con éxito el archivo %(filename)s de %(name)s."
#. ft stopped -#: ../src/gajim.py:1722 +#: ../src/gajim.py:1729 #, python-format msgid "File transfer of %(filename)s from %(name)s stopped." msgstr "Transferencia del archivo %(filename)s de %(name)s detenida."
-#: ../src/gajim.py:1735 +#: ../src/gajim.py:1742 #, python-format msgid "You successfully sent %(filename)s to %(name)s." msgstr "Has enviado con éxito el archivo %(filename)s a %(name)s."
#. ft stopped -#: ../src/gajim.py:1739 +#: ../src/gajim.py:1746 #, python-format msgid "File transfer of %(filename)s to %(name)s stopped." msgstr "Transferencia de %(filename)s a %(name)s detenida."
-#: ../src/gajim.py:1823 +#: ../src/gajim.py:1834 #, python-format msgid "" "Unable to decrypt message from %s\n" "It may have been tampered with." msgstr "" +"No se puede descifrar el mensaje de %s\n" +"Es posible que haya sido manipulado."
-#: ../src/gajim.py:1830 -#, fuzzy +#: ../src/gajim.py:1841 msgid "Unable to decrypt message" -msgstr "En cada _mensaje" +msgstr "No se puede descifrar el mensaje"
-#: ../src/gajim.py:1901 +#: ../src/gajim.py:1912 msgid "Username Conflict" msgstr "Conflicto con nombre de usuario"
-#: ../src/gajim.py:1902 +#: ../src/gajim.py:1913 msgid "Please type a new username for your local account" msgstr "Por favor, escribe un nuevo nombre de usuario para tu cuenta local"
-#: ../src/gajim.py:1914 +#: ../src/gajim.py:1925 msgid "Ping?" -msgstr "" +msgstr "Ping?"
-#: ../src/gajim.py:1927 +#: ../src/gajim.py:1938 #, python-format msgid "Pong! (%s s.)" -msgstr "" +msgstr "Pong! (%s s.)"
-#: ../src/gajim.py:1938 -#, fuzzy +#: ../src/gajim.py:1949 msgid "Error." -msgstr "Error: " +msgstr "Error."
-#: ../src/gajim.py:1963 -#, fuzzy +#: ../src/gajim.py:1974 msgid "Resource Conflict" -msgstr "Conflicto con nombre de usuario" +msgstr "Conflicto en el recurso"
-#: ../src/gajim.py:1964 +#: ../src/gajim.py:1975 msgid "" "You are already connected to this account with the same resource. Please " "type a new one" msgstr "" +"Ya estás conectado a esta cuenta con el mismo recurso. Por favor escribe uno " +"nuevo"
-#: ../src/gajim.py:2017 +#: ../src/gajim.py:2028 msgid "Error verifying SSL certificate" -msgstr "" +msgstr "Error verificando el certificado SSL"
-#: ../src/gajim.py:2018 +#: ../src/gajim.py:2029 #, python-format msgid "" "There was an error verifying the SSL certificate of your jabber server: %" "(error)s\n" "Do you still want to connect to this server?" msgstr "" +"Ocurrió un error verificando el certificado SSL de tu servidor jabber: %" +"(error)s\n" +"¿Aún quieres conectarte a este servidor?"
-#: ../src/gajim.py:2023 +#: ../src/gajim.py:2034 msgid "Ignore this error for this certificate." -msgstr "" +msgstr "Ignorar este error para este certificado"
-#: ../src/gajim.py:2038 +#: ../src/gajim.py:2049 msgid "SSL certificate error" -msgstr "" +msgstr "Error del certificado SSL"
-#: ../src/gajim.py:2039 +#: ../src/gajim.py:2050 #, python-format msgid "" "It seems the SSL certificate has changed or your connection is being " @@ -7973,86 +7871,98 @@ "\n" "Do you still want to connect and update the fingerprint of the certificate?" msgstr "" +"Parece que el certificado SSL fue cambiado o tu conexión está siendo " +"hackeada.\n" +"Antigua huella: %(old)s\n" +"Nueva huella: %(new)s\n" +"\n" +"¿Aún quieres conectar y actualizar la huella del certificado?"
-#: ../src/gajim.py:2061 ../src/gajim.py:2091 -#, fuzzy +#: ../src/gajim.py:2072 ../src/gajim.py:2102 msgid "Insecure connection" -msgstr "Conexión" +msgstr "Conexión no segura"
-#: ../src/gajim.py:2062 -#, fuzzy +#: ../src/gajim.py:2073 msgid "" "You are about to send your password on an unencrypted connection. Are you " "sure you want to do that?" -msgstr "Vas a crear un metacontacto. ¿Estás seguro de continuar?" +msgstr "" +"Vas a enviar tu contraseña en una conexión no cifrada- ¿Estás seguro de " +"querer hacer esto?"
-#: ../src/gajim.py:2064 ../src/gajim.py:2094 +#: ../src/gajim.py:2075 ../src/gajim.py:2105 msgid "Yes, I really want to connect insecurely" -msgstr "" +msgstr "Sí, quiero conectarme de forma no segura"
-#: ../src/gajim.py:2065 ../src/gajim.py:2095 ../src/groupchat_control.py:1843 -#: ../src/roster_window.py:3790 +#: ../src/gajim.py:2076 ../src/gajim.py:2106 ../src/groupchat_control.py:1844 +#: ../src/roster_window.py:3769 msgid "Do _not ask me again" msgstr "_No preguntarme otra vez"
-#: ../src/gajim.py:2092 +#: ../src/gajim.py:2103 msgid "" "You are about to send your password on an insecure connection. You should " "install PyOpenSSL to prevent that. Are you sure you want to do that?" msgstr "" +"Vas a enviar tu contraseña a través de una conexión no segura. Deberías " +"instalar PyOpenSSL para prevenirlo. ¿Estás seguro de que quieres hacerlo?"
-#: ../src/gajim.py:2109 +#: ../src/gajim.py:2120 msgid "PEP node was not removed" -msgstr "" +msgstr "El nodo PEP no fue eliminado"
-#: ../src/gajim.py:2110 +#: ../src/gajim.py:2121 #, python-format msgid "PEP node %(node)s was not removed: %(message)s" -msgstr "" +msgstr "El nodo PEP %(node)s no se pudo eliminar: %(message)s"
#. theme doesn't exist, disable emoticons -#: ../src/gajim.py:2575 ../src/gajim.py:2596 -#, fuzzy +#: ../src/gajim.py:2589 ../src/gajim.py:2611 msgid "Emoticons disabled" -msgstr "Encriptación desactivada" +msgstr "Emoticonos desactivados"
-#: ../src/gajim.py:2576 +#: ../src/gajim.py:2590 msgid "" "Your configured emoticons theme has not been found, so emoticons have been " "disabled." msgstr "" +"No se pudo encontrar tu tema de emoticonos configurado, los emoticonos han " +"sido desactivados."
-#: ../src/gajim.py:2597 +#: ../src/gajim.py:2612 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 " "for more details." msgstr "" +"No se pudo cargar tu tema de emoticonos configurado. Podrías necesitar " +"actualizar el formato del archivo emoticons.py. Mira http://trac.gajim.org/" +"wiki/Emoticons para más detalles."
-#: ../src/gajim.py:2623 ../src/roster_window.py:3324 +#: ../src/gajim.py:2638 ../src/roster_window.py:3303 msgid "You cannot join a group chat while you are invisible" msgstr "No puedes entrar a un grupo de charla estando invisible"
#. it is good to notify the user #. in case he or she cannot see the output of the console -#: ../src/gajim.py:2903 +#: ../src/gajim.py:2917 msgid "Could not save your settings and preferences" msgstr "No se pueden guardar las preferencias"
-#: ../src/gajim.py:3000 +#: ../src/gajim.py:3014 msgid "Bookmark already set" msgstr "Marcador ya definido"
-#: ../src/gajim.py:3001 +#: ../src/gajim.py:3015 #, python-format msgid "Group Chat "%s" is already in your bookmarks." msgstr "El salón "%s" ya se encuentra en tus marcadores."
-#: ../src/gajim.py:3014 +#: ../src/gajim.py:3028 msgid "Bookmark has been added successfully" msgstr "Marcador añadido con éxito"
-#: ../src/gajim.py:3015 +#: ../src/gajim.py:3029 msgid "You can manage your bookmarks via Actions menu in your roster." msgstr "Puedes gestionar tus marcadores mediante el menú Acciones de tu roster"
@@ -8074,18 +7984,16 @@ msgstr "Muestra u oculta la ventana de la lista de contactos"
#: ../src/gajim-remote.py:91 -#, fuzzy msgid "Pops up a window with the next pending event" msgstr "Levanta una venana con el siguiente evento pendiente"
#: ../src/gajim-remote.py:95 -#, fuzzy msgid "" "Prints a list of all contacts in the roster. Each contact appears on a " "separate line" msgstr "" "Muestra una lista de todos los contactos en el roster. Cada contacto aparece " -"en una línea distinta" +"en una línea separada"
#: ../src/gajim-remote.py:98 ../src/gajim-remote.py:113 #: ../src/gajim-remote.py:123 ../src/gajim-remote.py:136 @@ -8175,7 +8083,7 @@
#: ../src/gajim-remote.py:134 ../src/gajim-remote.py:148 msgid "if specified, the message will be encrypted using this public key" -msgstr "si es especificado, el mensaje será encriptado usando su clave pública" +msgstr "si es especificado, el mensaje será cifrado usando su clave pública"
#: ../src/gajim-remote.py:136 ../src/gajim-remote.py:150 #: ../src/gajim-remote.py:159 @@ -8202,12 +8110,11 @@
#: ../src/gajim-remote.py:155 msgid "Sends new message to a groupchat you've joined." -msgstr "" +msgstr "Envía un nuevo mensaje a un grupo de charla al que hayas entrado"
#: ../src/gajim-remote.py:157 -#, fuzzy msgid "JID of the room that will receive the message" -msgstr "la JID del contacto que recibirá el mensaje" +msgstr "La JID del salón que recibirá el mensaje"
#: ../src/gajim-remote.py:164 msgid "Gets detailed info on a contact" @@ -8287,7 +8194,7 @@
#: ../src/gajim-remote.py:218 msgid "jid" -msgstr "" +msgstr "jid"
#: ../src/gajim-remote.py:219 msgid "Adds new contact to this account" @@ -8305,7 +8212,6 @@ "cuenta)"
#: ../src/gajim-remote.py:238 -#, fuzzy msgid "Returns number of unread messages" msgstr "Devuelve el número de mensajes sin leer"
@@ -8335,68 +8241,59 @@
#: ../src/gajim-remote.py:257 msgid "Handle a xmpp:/ uri" -msgstr "" +msgstr "Manejar una uri xmpp:/"
#: ../src/gajim-remote.py:259 msgid "uri" -msgstr "" +msgstr "uri"
#: ../src/gajim-remote.py:259 msgid "URI to handle" -msgstr "" +msgstr "URI a manejar"
#: ../src/gajim-remote.py:260 msgid "Account in which you want to handle it" -msgstr "" +msgstr "La cuenta que quieres manejar"
#: ../src/gajim-remote.py:265 -#, fuzzy msgid "Join a MUC room" -msgstr "_Entrar en un nuevo salón" +msgstr "Entrar en un salón MUC"
#: ../src/gajim-remote.py:267 -#, fuzzy msgid "room" -msgstr "De" +msgstr "salón"
#: ../src/gajim-remote.py:267 -#, fuzzy msgid "Room JID" -msgstr "Salón:" +msgstr "JID del salón"
#: ../src/gajim-remote.py:268 -#, fuzzy msgid "nick" -msgstr "Alias" +msgstr "alias"
#: ../src/gajim-remote.py:268 -#, fuzzy msgid "Nickname to use" -msgstr "Alias no encontrado: %s" +msgstr "Alias a usar"
#: ../src/gajim-remote.py:269 -#, fuzzy msgid "password" -msgstr "Contraseña: " +msgstr "contraseña"
#: ../src/gajim-remote.py:269 -#, fuzzy msgid "Password to enter the room" -msgstr "Las contraseñas no coinciden" +msgstr "Contraseña para entrar al salón"
#: ../src/gajim-remote.py:270 msgid "Account from which you want to enter the room" -msgstr "" +msgstr "La cuenta desde la que quieres entrar al salón"
#: ../src/gajim-remote.py:275 -#, fuzzy msgid "Check if Gajim is running" -msgstr "Por favor, comprueba si avahi-daemon se encuentra en ejecución." +msgstr "Comprueba si Gajim se encuentra en ejecución"
#: ../src/gajim-remote.py:279 -#, fuzzy msgid "Shows or hides the ipython window" -msgstr "Muestra u oculta la ventana de la lista de contactos" +msgstr "Muestra u oculta la ventana ipython"
#: ../src/gajim-remote.py:306 msgid "Missing argument "contact_jid"" @@ -8417,25 +8314,27 @@
#: ../src/gajim-remote.py:380 msgid "It seems Gajim is not running. So you can't use gajim-remote." -msgstr "" +msgstr "Parece que Gajim no está en ejecución. No puedes usar gajim-remote."
-#: ../src/gajim-remote.py:409 +#: ../src/gajim-remote.py:403 #, python-format msgid "" "Usage: %(basename)s %(command)s %(arguments)s \n" "\t %(help)s" msgstr "" +"Uso: %(basename)s %(command)s %(arguments)s \n" +"\t %(help)s"
-#: ../src/gajim-remote.py:413 +#: ../src/gajim-remote.py:407 msgid "Arguments:" msgstr "Argumentos"
-#: ../src/gajim-remote.py:417 +#: ../src/gajim-remote.py:411 #, python-format msgid "%s not found" msgstr "%s no encontrado"
-#: ../src/gajim-remote.py:421 +#: ../src/gajim-remote.py:415 #, python-format msgid "" "Usage: %s command [arguments]\n" @@ -8444,41 +8343,39 @@ "Modo de uso: %s comando [argumentos]\n" "Comando es uno de:\n"
-#: ../src/gajim-remote.py:493 -#, fuzzy, python-format +#: ../src/gajim-remote.py:480 +#, python-format msgid "" "Too many arguments. \n" "Type "%(basename)s help %(command)s" for more info" msgstr "" -"El argumento "%s" no se ha especificado. \n" -"Escribe "%s help %s" para más información" +"Demasiados argumentos. \n" +"Escribe "%(basename)s help %(command)s" para más información"
-#: ../src/gajim-remote.py:498 -#, fuzzy, python-format +#: ../src/gajim-remote.py:485 +#, python-format msgid "" "Argument "%(arg)s" is not specified. \n" "Type "%(basename)s help %(command)s" for more info" msgstr "" -"El argumento "%s" no se ha especificado. \n" -"Escribe "%s help %s" para más información" +"No se especificó el argumento "%(arg)s". \n" +"Escribe "%(basename)s help %(command)s" para más información"
-#: ../src/gajim-remote.py:517 -#, fuzzy +#: ../src/gajim-remote.py:504 msgid "Wrong uri" -msgstr "Host erróneo" +msgstr "Uri incorrecta"
#: ../src/gajim_themes_window.py:59 msgid "Theme" msgstr "Tema"
#: ../src/gajim_themes_window.py:97 -#, fuzzy msgid "You cannot make changes to the default theme" -msgstr "No puedes eliminar el tema actual" +msgstr "No puedes realizar cambios en el tema por defecto"
#: ../src/gajim_themes_window.py:98 msgid "Please create a clean new theme with your desired name." -msgstr "" +msgstr "Por favor crea un nuevo tema desde cero con el nombre deseado."
#. don't confuse translators #: ../src/gajim_themes_window.py:172 @@ -8493,152 +8390,150 @@ msgid "Please first choose another for your current theme." msgstr "Por favor, primero elige otro distinto como tema actual."
-#: ../src/groupchat_control.py:150 +#: ../src/groupchat_control.py:152 msgid "Sending private message failed" msgstr "Falló el envío del mensaje privado"
#. in second %s code replaces with nickname -#: ../src/groupchat_control.py:152 -#, fuzzy, python-format +#: ../src/groupchat_control.py:154 +#, python-format msgid "You are no longer in group chat "%(room)s" or "%(nick)s" has left." -msgstr "Ya no estás en el grupo de charla "%s" o bien "%s" ya no existe." +msgstr "" +"Ya no estás en el grupo de charla "%(room)s" o "%(nick)s" se ha ido."
-#: ../src/groupchat_control.py:451 +#: ../src/groupchat_control.py:453 msgid "Insert Nickname" msgstr "Insertar alias"
-#: ../src/groupchat_control.py:603 -#, fuzzy +#: ../src/groupchat_control.py:605 msgid "Conversation with " -msgstr "Histórico de conversaciones" +msgstr "Conversación con "
-#: ../src/groupchat_control.py:605 -#, fuzzy +#: ../src/groupchat_control.py:607 msgid "Continued conversation" -msgstr "Conexión" +msgstr "Conversación continuada"
-#: ../src/groupchat_control.py:1027 +#: ../src/groupchat_control.py:1028 msgid "Really send file?" -msgstr "" +msgstr "¿Enviar fichero?"
-#: ../src/groupchat_control.py:1028 +#: ../src/groupchat_control.py:1029 #, python-format msgid "If you send a file to %s, he/she will know your real Jabber ID." -msgstr "" +msgstr "Si envías un archivo a %s, el/ella sabrá tu ID de Jabber real."
#. Can be a message (see handle_event_gc_config_change in gajim.py) -#: ../src/groupchat_control.py:1127 +#: ../src/groupchat_control.py:1128 msgid "Room logging is enabled" -msgstr "" +msgstr "Registro del salón activado"
-#: ../src/groupchat_control.py:1129 -#, fuzzy +#: ../src/groupchat_control.py:1130 msgid "A new room has been created" -msgstr "Tu nueva cuenta ha sido creada con éxito" +msgstr "Se ha creado un nuevo salón"
-#: ../src/groupchat_control.py:1132 +#: ../src/groupchat_control.py:1133 msgid "The server has assigned or modified your roomnick" -msgstr "" +msgstr "El servidor ha asignado o modificado tu alias en el salón"
#. do not print 'kicked by None' -#: ../src/groupchat_control.py:1138 +#: ../src/groupchat_control.py:1139 #, python-format msgid "%(nick)s has been kicked: %(reason)s" msgstr "%(nick)s ha sido expulsado por %(reason)s"
-#: ../src/groupchat_control.py:1142 +#: ../src/groupchat_control.py:1143 #, python-format msgid "%(nick)s has been kicked by %(who)s: %(reason)s" msgstr "%(nick)s ha sido expulsado por %(who)s: %(reason)s"
#. do not print 'banned by None' -#: ../src/groupchat_control.py:1149 +#: ../src/groupchat_control.py:1150 #, python-format msgid "%(nick)s has been banned: %(reason)s" msgstr "%(nick)s ha sido expulsado: %(reason)s"
-#: ../src/groupchat_control.py:1153 +#: ../src/groupchat_control.py:1154 #, python-format msgid "%(nick)s has been banned by %(who)s: %(reason)s" msgstr "%(nick)s ha sido expulsado por %(who)s: %(reason)s"
-#: ../src/groupchat_control.py:1162 +#: ../src/groupchat_control.py:1163 #, python-format msgid "You are now known as %s" msgstr "Eres ahora conocido como %s"
-#: ../src/groupchat_control.py:1200 ../src/groupchat_control.py:1204 -#: ../src/groupchat_control.py:1209 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1201 ../src/groupchat_control.py:1205 +#: ../src/groupchat_control.py:1210 +#, python-format msgid "%(nick)s has been removed from the room (%(reason)s)" -msgstr "%(nick)s ha sido expulsado por %(who)s: %(reason)s" +msgstr "%(nick)s ha sido eliminado del salón (%(reason)s)"
-#: ../src/groupchat_control.py:1201 -#, fuzzy +#: ../src/groupchat_control.py:1202 msgid "affiliation changed" -msgstr "Afiliación: " +msgstr "cambió la afiliación"
-#: ../src/groupchat_control.py:1206 +#: ../src/groupchat_control.py:1207 msgid "room configuration changed to members-only" -msgstr "" +msgstr "la configuración del salón cambió a sólo miembros"
-#: ../src/groupchat_control.py:1211 +#: ../src/groupchat_control.py:1212 msgid "system shutdown" -msgstr "" +msgstr "apagado del sistema"
-#: ../src/groupchat_control.py:1280 +#: ../src/groupchat_control.py:1281 #, python-format msgid "** Affiliation of %(nick)s has been set to %(affiliation)s by %(actor)s" msgstr "" +"** La afiliación de %(nick)s ha sido definida en &(affiliation)s por %(actor)" +"s"
-#: ../src/groupchat_control.py:1284 +#: ../src/groupchat_control.py:1285 #, python-format msgid "** Affiliation of %(nick)s has been set to %(affiliation)s" -msgstr "" +msgstr "** La afiliación de %(nick)s ha sido definida en %(affiliation)s"
-#: ../src/groupchat_control.py:1299 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1300 +#, python-format msgid "** Role of %(nick)s has been set to %(role)s by %(actor)s" -msgstr "%(nick)s ha sido expulsado por %(who)s: %(reason)s" +msgstr "** El rol de %(nick)s ha sido fijado a %(role)s por %(actor)s"
-#: ../src/groupchat_control.py:1303 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1304 +#, python-format msgid "** Role of %(nick)s has been set to %(role)s" -msgstr "%(nick)s ha sido expulsado por %(reason)s" +msgstr "** El rol de %(nick)s ha sido fijado a %(role)s"
-#: ../src/groupchat_control.py:1332 +#: ../src/groupchat_control.py:1333 #, python-format msgid "%s has left" msgstr "%s ha salido"
-#: ../src/groupchat_control.py:1337 +#: ../src/groupchat_control.py:1338 #, python-format msgid "%s has joined the group chat" msgstr "%s ha entrado en el grupo de charla"
#. Invalid Nickname #. invalid char -#: ../src/groupchat_control.py:1452 ../src/groupchat_control.py:1733 -#, fuzzy +#: ../src/groupchat_control.py:1453 ../src/groupchat_control.py:1734 msgid "Invalid nickname" -msgstr "Nombre de usuario no válido" +msgstr "Alias no válido"
-#: ../src/groupchat_control.py:1476 ../src/groupchat_control.py:1494 -#: ../src/groupchat_control.py:1578 ../src/groupchat_control.py:1595 +#: ../src/groupchat_control.py:1477 ../src/groupchat_control.py:1495 +#: ../src/groupchat_control.py:1579 ../src/groupchat_control.py:1596 #, python-format msgid "Nickname not found: %s" msgstr "Alias no encontrado: %s"
-#: ../src/groupchat_control.py:1510 +#: ../src/groupchat_control.py:1511 msgid "This group chat has no subject" msgstr "Este grupo de charla no tiene tema"
-#: ../src/groupchat_control.py:1521 +#: ../src/groupchat_control.py:1522 #, python-format msgid "Invited %(contact_jid)s to %(room_jid)s." msgstr "%(contact_jid)s invitado a %(room_jid)s."
-#: ../src/groupchat_control.py:1656 +#: ../src/groupchat_control.py:1657 #, python-format msgid "" "Usage: /%s <nickname|JID> [reason], bans the JID from the group chat. The " @@ -8651,8 +8546,8 @@ "está actualmente en el grupo de conversación, el/ella será también " "expulsado. NO soporta espacio en el alias."
-#: ../src/groupchat_control.py:1663 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1664 +#, python-format msgid "" "Usage: /%s <nickname>, opens a private chat window with the specified " "occupant." @@ -8660,7 +8555,7 @@ "Uso: /%s <alias>, abre una ventana de conversación con el ocupante " "especificado."
-#: ../src/groupchat_control.py:1669 +#: ../src/groupchat_control.py:1670 #, python-format msgid "" "Usage: /%s [reason], closes the current window or tab, displaying reason if " @@ -8669,7 +8564,7 @@ "Uso: /%s [motivo], cierra la ventana o pestaña actual, mostrando el motivo " "si se especifica."
-#: ../src/groupchat_control.py:1675 +#: ../src/groupchat_control.py:1676 #, python-format msgid "" "Usage: /%s <JID> [reason], invites JID to the current group chat, optionally " @@ -8678,7 +8573,7 @@ "Uso: /%s <JID> [reason], invita al JID al grupo de charla actual, " "opcionalmente proveyendo un motivo."
-#: ../src/groupchat_control.py:1679 +#: ../src/groupchat_control.py:1680 #, python-format msgid "" "Usage: /%s <room>@<server>[/nickname], offers to join room@server optionally " @@ -8687,7 +8582,7 @@ "Uso: /%s <salón>@<servidor>[/alias], ofrece la entrada a salón@servidor " "opcionalmente usando el alias especificado"
-#: ../src/groupchat_control.py:1683 +#: ../src/groupchat_control.py:1684 #, python-format msgid "" "Usage: /%s <nickname> [reason], removes the occupant specified by nickname " @@ -8697,77 +8592,79 @@ "Uso: /%s <alias> [motivo], elimina del salón al ocupante especificado por el " "alias y opcionalmente muestra un motivo. NO soporta espacios en el alias."
-#: ../src/groupchat_control.py:1692 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1693 +#, python-format msgid "" "Usage: /%s <nickname> [message], opens a private message window and sends " "message to the occupant specified by nickname." msgstr "" -"Uso: /%s <alias> [message], abre una ventana de conversación privada y envía " -"un mensaje al ocupante especificado por el alias." +"Uso: /%s <alias> [mensaje], abre una ventana de conversación y envía mensaje " +"al ocupante especificado por el alias."
-#: ../src/groupchat_control.py:1697 +#: ../src/groupchat_control.py:1698 #, python-format msgid "Usage: /%s <nickname>, changes your nickname in current group chat." msgstr "Uso: /%s <alias>, cambia tu alias en el salón actual."
-#: ../src/groupchat_control.py:1701 +#: ../src/groupchat_control.py:1702 #, python-format msgid "Usage: /%s , display the names of group chat occupants." msgstr "Uso: /%s , muestra los nombres de los ocupantes del grupo de charla."
-#: ../src/groupchat_control.py:1705 +#: ../src/groupchat_control.py:1706 #, python-format msgid "Usage: /%s [topic], displays or updates the current group chat topic." msgstr "Uso: /%s [tema], muestra o actualiza el tema actual del salón."
-#: ../src/groupchat_control.py:1708 +#: ../src/groupchat_control.py:1709 #, python-format msgid "" "Usage: /%s <message>, sends a message without looking for other commands." msgstr "Uso: /%s <mensaje>, envía un mensaje sin buscar otros comandos."
-#: ../src/groupchat_control.py:1837 +#: ../src/groupchat_control.py:1838 #, python-format msgid "Are you sure you want to leave group chat "%s"?" msgstr "¿Estás seguro de querer abandonar el salón "%s"?"
-#: ../src/groupchat_control.py:1839 +#: ../src/groupchat_control.py:1840 msgid "" "If you close this window, you will be disconnected from this group chat." msgstr "Si cierras esta ventana, serás desconectado de este grupo de charla."
-#: ../src/groupchat_control.py:1876 +#: ../src/groupchat_control.py:1877 msgid "Changing Subject" msgstr "Cambiando el tema"
-#: ../src/groupchat_control.py:1877 +#: ../src/groupchat_control.py:1878 msgid "Please specify the new subject:" msgstr "Especifica el nuevo tema:"
-#: ../src/groupchat_control.py:1881 +#: ../src/groupchat_control.py:1882 msgid "Changing Nickname" msgstr "Cambiando alias"
-#: ../src/groupchat_control.py:1882 +#: ../src/groupchat_control.py:1883 msgid "Please specify the new nickname you want to use:" msgstr "Especifica el nuevo alias que quieres usar:"
#. Ask for a reason -#: ../src/groupchat_control.py:1908 -#, fuzzy, python-format +#: ../src/groupchat_control.py:1909 +#, python-format msgid "Destroying %s" -msgstr "Descripción: %s" +msgstr "Destruyendo %s"
-#: ../src/groupchat_control.py:1909 +#: ../src/groupchat_control.py:1910 msgid "" "You are going to definitively destroy this room.\n" "You may specify a reason below:" msgstr "" +"Vas a destruir definitivamente este salón.\n" +"Puedes especificar una razón abajo:"
-#: ../src/groupchat_control.py:1911 +#: ../src/groupchat_control.py:1912 msgid "You may also enter an alternate venue:" -msgstr "" +msgstr "También puedes introducir un lugar alternativo:"
#. ask for reason #: ../src/groupchat_control.py:2083 @@ -8775,12 +8672,12 @@ msgid "Kicking %s" msgstr "Expulsando a %s"
-#: ../src/groupchat_control.py:2084 ../src/groupchat_control.py:2388 +#: ../src/groupchat_control.py:2084 ../src/groupchat_control.py:2376 msgid "You may specify a reason below:" msgstr "Debes especificar un motivo debajo:"
#. ask for reason -#: ../src/groupchat_control.py:2387 +#: ../src/groupchat_control.py:2375 #, python-format msgid "Banning %s" msgstr "Expulsando a %s" @@ -8829,18 +8726,17 @@
#. xmpp: is currently handled by another program, so ask the user #: ../src/gtkgui_helpers.py:733 -#, fuzzy msgid "Gajim is not the default Jabber client" -msgstr "Gajim - Un cliente de Jabber en GTK+" +msgstr "Gajim no es el cliente por defecto de Jabber"
#: ../src/gtkgui_helpers.py:734 -#, fuzzy msgid "Would you like to make Gajim the default Jabber client?" -msgstr "¿Quieres sobreescribirlo?" +msgstr "¿Quieres hacer de Gajim el cliente por defecto de Jabber?"
#: ../src/gtkgui_helpers.py:735 msgid "Always check to see if Gajim is the default Jabber client on startup" msgstr "" +"Comprobar siempre al inicio si Gajim es el cliente por defecto de Jabber"
#: ../src/gtkgui_helpers.py:804 msgid "Extension not supported" @@ -8853,7 +8749,7 @@ "La imagen no se pudo guardar en formato %(type)s. ¿Guardar como %" "(new_filename)s?"
-#: ../src/gtkgui_helpers.py:844 +#: ../src/gtkgui_helpers.py:840 msgid "Save Image as..." msgstr "Guardar imagen como..."
@@ -8905,89 +8801,87 @@ "\n" "En caso de elegir SI, por favor, espera..."
-#: ../src/history_manager.py:437 +#: ../src/history_manager.py:434 msgid "Exporting History Logs..." msgstr "Exportando historial..."
-#: ../src/history_manager.py:512 +#: ../src/history_manager.py:509 #, python-format msgid "%(who)s on %(time)s said: %(message)s\n" msgstr "%(who)s - %(time)s dijo: %(message)s\n"
-#: ../src/history_manager.py:549 +#: ../src/history_manager.py:546 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] "¿Realmente quieres eliminar el historial del contacto seleccionado?" msgstr[1] "" "¿Realmente quieres eliminar el historial de los contactos seleccionados?"
-#: ../src/history_manager.py:553 ../src/history_manager.py:588 +#: ../src/history_manager.py:550 ../src/history_manager.py:585 msgid "This is an irreversible operation." msgstr "Esta es una operación irreversible"
-#: ../src/history_manager.py:585 +#: ../src/history_manager.py:582 msgid "Do you really want to delete the selected message?" msgid_plural "Do you really want to delete the selected messages?" msgstr[0] "¿Realmente quieres eliminar el mensaje seleccionado?" msgstr[1] "¿Realmente quieres eliminar los mensajes seleccionados?"
-#: ../src/history_window.py:287 +#: ../src/history_window.py:298 #, python-format msgid "Conversation History with %s" msgstr "Histórico de conversaciones con %s"
-#: ../src/history_window.py:332 -#, fuzzy +#: ../src/history_window.py:343 msgid "Disk Error" -msgstr "Error: " +msgstr "Error de disco"
-#: ../src/history_window.py:413 +#: ../src/history_window.py:424 #, python-format msgid "%(nick)s is now %(status)s: %(status_msg)s" msgstr "%(nick)s está ahora %(status)s: %(status_msg)s"
-#: ../src/history_window.py:423 +#: ../src/history_window.py:434 #, python-format msgid "Status is now: %(status)s: %(status_msg)s" msgstr "El estado es ahora: %(status)s: %(status_msg)s "
-#: ../src/history_window.py:426 +#: ../src/history_window.py:437 #, python-format msgid "Status is now: %(status)s" msgstr "El estado es ahora: %(status)s"
#: ../src/htmltextview.py:591 ../src/htmltextview.py:602 -#, fuzzy msgid "Timeout loading image" -msgstr "No se pudo cargar la imagen" +msgstr "Expiró el tiempo para cargar la imagen"
#: ../src/htmltextview.py:612 msgid "Image is too big" msgstr "La imagen es demasiado grande"
-#: ../src/message_window.py:446 +#: ../src/message_window.py:433 msgid "Chats" msgstr "Charlas"
-#: ../src/message_window.py:448 +#: ../src/message_window.py:435 msgid "Group Chats" msgstr "Grupos de charla"
-#: ../src/message_window.py:450 +#: ../src/message_window.py:437 msgid "Private Chats" msgstr "Conversaciones privadas"
-#: ../src/message_window.py:456 +#: ../src/message_window.py:443 msgid "Messages" msgstr "Mensajes"
-#: ../src/negotiation.py:34 +#: ../src/negotiation.py:32 msgid "- messages will be logged" -msgstr "" +msgstr "- los mensajes serán registrados"
-#: ../src/negotiation.py:36 +#: ../src/negotiation.py:34 msgid "- messages will not be logged" -msgstr "" +msgstr "- los mensajes no serán registrados"
#: ../src/notify.py:230 #, python-format @@ -9017,12 +8911,12 @@ #: ../src/notify.py:270 #, python-format msgid "%(nickname)s: %(message)s" -msgstr "" +msgstr "%(nickname)s: %(message)s"
#: ../src/notify.py:273 -#, fuzzy, python-format +#, python-format msgid "Messaged by %(nickname)s" -msgstr "Nuevo mensaje de %(nickname)s" +msgstr "Enviado por %(nickname)s"
#: ../src/notify.py:279 #, python-format @@ -9030,52 +8924,49 @@ msgstr "Nuevo mensaje de %(nickname)s"
#: ../src/osx/growler.py:10 -#, fuzzy msgid "Generic" -msgstr "General" +msgstr "Genérico"
-#: ../src/profile_window.py:56 +#: ../src/profile_window.py:55 msgid "Retrieving profile..." msgstr "Recuperando perfil..."
-#: ../src/profile_window.py:109 ../src/roster_window.py:2787 -#, fuzzy +#: ../src/profile_window.py:108 ../src/roster_window.py:2768 msgid "File is empty" -msgstr "Ruta del archivo" +msgstr "El archivo está vacío"
-#: ../src/profile_window.py:112 ../src/roster_window.py:2790 -#, fuzzy +#: ../src/profile_window.py:111 ../src/roster_window.py:2771 msgid "File does not exist" -msgstr "No existe el grupo de charla." +msgstr "El archivo no existe"
#. keep identation #. unknown format -#: ../src/profile_window.py:126 ../src/profile_window.py:142 -#: ../src/roster_window.py:2792 ../src/roster_window.py:2803 +#: ../src/profile_window.py:125 ../src/profile_window.py:141 +#: ../src/roster_window.py:2773 ../src/roster_window.py:2784 msgid "Could not load image" msgstr "No se pudo cargar la imagen"
-#: ../src/profile_window.py:252 +#: ../src/profile_window.py:251 msgid "Information received" msgstr "Información recibida"
-#: ../src/profile_window.py:321 +#: ../src/profile_window.py:318 msgid "Without a connection you can not publish your contact information." msgstr "Sin una conexión no puedes publicar tu información de contacto."
-#: ../src/profile_window.py:335 +#: ../src/profile_window.py:332 msgid "Sending profile..." msgstr "Enviando perfil..."
-#: ../src/profile_window.py:350 +#: ../src/profile_window.py:347 msgid "Information NOT published" msgstr "Información NO publicada"
-#: ../src/profile_window.py:357 +#: ../src/profile_window.py:354 msgid "vCard publication failed" msgstr "falló la publicación de la vCard"
-#: ../src/profile_window.py:358 +#: ../src/profile_window.py:355 msgid "" "There was an error while publishing your personal information, try again " "later." @@ -9083,52 +8974,55 @@ "Ocurrió un error mientras se publicaba tu información personal, inténtalo de " "nuevo más tarde."
-#: ../src/roster_window.py:288 ../src/roster_window.py:945 +#: ../src/roster_window.py:286 ../src/roster_window.py:940 msgid "Merged accounts" msgstr "Cuentas combinadas"
-#: ../src/roster_window.py:1860 +#: ../src/roster_window.py:1850 msgid "Authorization has been sent" msgstr "La autorización ha sido enviada"
-#: ../src/roster_window.py:1861 +#: ../src/roster_window.py:1851 #, python-format msgid "Now "%s" will know your status." msgstr "Ahora "%s" podrá saber tu estado."
-#: ../src/roster_window.py:1881 +#: ../src/roster_window.py:1871 msgid "Subscription request has been sent" msgstr "La petición de subscripción ha sido enviada"
-#: ../src/roster_window.py:1882 +#: ../src/roster_window.py:1872 #, python-format msgid "If "%s" accepts this request you will know his or her status." msgstr "Si "%s" acepta esta petición podrás saber su estado."
-#: ../src/roster_window.py:1894 +#: ../src/roster_window.py:1884 msgid "Authorization has been removed" msgstr "La autorización ha sido eliminada"
-#: ../src/roster_window.py:1895 +#: ../src/roster_window.py:1885 #, python-format msgid "Now "%s" will always see you as offline." msgstr "Ahora "%s" siempre te verá desconectado."
-#: ../src/roster_window.py:1920 +#: ../src/roster_window.py:1909 msgid "" "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 está instalado pero no fue \t\t\t\t\t\t\tiniciado " +"correctamente (la variable de entorno probablemente no fue \t\t\t\t\t\t" +"\tcorrectamente definida)"
-#: ../src/roster_window.py:1930 ../src/roster_window.py:1950 +#: ../src/roster_window.py:1919 ../src/roster_window.py:1939 msgid "GPG is not usable" -msgstr "" +msgstr "GPG no es usable"
-#: ../src/roster_window.py:2119 ../src/roster_window.py:3267 +#: ../src/roster_window.py:2103 ../src/roster_window.py:3247 msgid "You are participating in one or more group chats" msgstr "Estás participando en uno o más grupos de charla"
-#: ../src/roster_window.py:2120 ../src/roster_window.py:3268 +#: ../src/roster_window.py:2104 ../src/roster_window.py:3248 msgid "" "Changing your status to invisible will result in disconnection from those " "group chats. Are you sure you want to go invisible?" @@ -9136,100 +9030,96 @@ "Cambiando tu estado a invisible provocará tu desconexión de los grupos de " "charla. ¿Estás seguro de querer ser invisible?"
-#: ../src/roster_window.py:2146 +#: ../src/roster_window.py:2130 msgid "desync'ed" -msgstr "" +msgstr "desincronizado"
-#: ../src/roster_window.py:2284 ../src/roster_window.py:2521 +#: ../src/roster_window.py:2268 ../src/roster_window.py:2505 msgid "You have unread messages" msgstr "Tienes mensajes sin leer"
-#: ../src/roster_window.py:2285 -#, fuzzy +#: ../src/roster_window.py:2269 msgid "" "Messages will only be available for reading them later if you have history " "enabled and contact is in your roster." msgstr "" "Los mensajes sólo estarán disponibles para lectura posterior si tienes el " -"histórico activado" +"histórico activado y el contacto está en tu roster."
-#: ../src/roster_window.py:2522 +#: ../src/roster_window.py:2506 msgid "You must read them before removing this transport." msgstr "Debes leerlos antes de eliminar este transporte."
-#: ../src/roster_window.py:2525 +#: ../src/roster_window.py:2509 #, python-format msgid "Transport "%s" will be removed" msgstr "El transporte "%s" será eliminado"
-#: ../src/roster_window.py:2526 -#, fuzzy +#: ../src/roster_window.py:2510 msgid "" "You will no longer be able to send and receive messages from contacts using " "this transport." msgstr "" -"Ya no podrás enviar y recibir mensajes a contactos desde este transporte" +"Ya no podrás enviar y recibir mensajes de contactos que usen este transporte"
-#: ../src/roster_window.py:2529 +#: ../src/roster_window.py:2513 msgid "Transports will be removed" msgstr "Los transportes serán eliminados"
-#: ../src/roster_window.py:2534 -#, fuzzy, python-format +#: ../src/roster_window.py:2518 +#, python-format msgid "" "You will no longer be able to send and receive messages to contacts from " "these transports: %s" msgstr "" -"Ya no podrás enviar y recibir mensajes a contactos desde estos transportes: %" -"s" +"Ya no podrás enviar y recibir mensajes a contactos de estos transportes: %s"
#. it's jid -#: ../src/roster_window.py:2668 +#: ../src/roster_window.py:2649 msgid "Rename Contact" msgstr "Renombrar contacto"
-#: ../src/roster_window.py:2669 +#: ../src/roster_window.py:2650 #, python-format msgid "Enter a new nickname for contact %s" msgstr "Introduce un nuevo alias para el contacto %s"
-#: ../src/roster_window.py:2676 +#: ../src/roster_window.py:2657 msgid "Rename Group" msgstr "Renombrar grupo"
-#: ../src/roster_window.py:2677 +#: ../src/roster_window.py:2658 #, python-format msgid "Enter a new name for group %s" msgstr "Introduce un nuevo nombre para el grupo %s"
-#: ../src/roster_window.py:2733 +#: ../src/roster_window.py:2714 msgid "Remove Group" msgstr "Eliminar grupo"
-#: ../src/roster_window.py:2734 +#: ../src/roster_window.py:2715 #, python-format msgid "Do you want to remove group %s from the roster?" msgstr "¿Quieres eliminar el grupo %s del roster?"
-#: ../src/roster_window.py:2735 -#, fuzzy +#: ../src/roster_window.py:2716 msgid "Also remove all contacts in this group from your roster" msgstr "Eliminar del roster también a todos los contactos dentro del grupo"
-#: ../src/roster_window.py:2774 +#: ../src/roster_window.py:2755 msgid "Assign OpenPGP Key" msgstr "Asignar clave OpenPGP"
-#: ../src/roster_window.py:2775 +#: ../src/roster_window.py:2756 msgid "Select a key to apply to the contact" msgstr "Selecciona una clave para ser aplicada al contacto"
-#: ../src/roster_window.py:3128 +#: ../src/roster_window.py:3108 #, python-format msgid "Contact "%s" will be removed from your roster" msgstr "El contacto "%s" será eliminado de tu roster"
-#: ../src/roster_window.py:3132 +#: ../src/roster_window.py:3112 msgid "" "By removing this contact you also remove authorization resulting in him or " "her always seeing you as offline." @@ -9238,12 +9128,11 @@ "resultante y el/ella siempre te verán desconectado."
#. Contact is not in roster -#: ../src/roster_window.py:3137 -#, fuzzy +#: ../src/roster_window.py:3117 msgid "Do you want to continue?" -msgstr "¿Qué quieres hacer?" +msgstr "¿Quieres continuar?"
-#: ../src/roster_window.py:3141 +#: ../src/roster_window.py:3121 msgid "" "By removing this contact you also by default remove authorization resulting " "in him or her always seeing you as offline." @@ -9251,16 +9140,16 @@ "Eliminando este contacto también se está eliminando la autorización. Este " "contacto siempre te verá desconectado."
-#: ../src/roster_window.py:3144 +#: ../src/roster_window.py:3124 msgid "I want this contact to know my status after removal" msgstr "Quiero que este contacto conozca mi estado después de la eliminación"
#. several contact to remove at the same time -#: ../src/roster_window.py:3148 +#: ../src/roster_window.py:3128 msgid "Contacts will be removed from your roster" msgstr "Los contactos serán eliminados de tu roster"
-#: ../src/roster_window.py:3152 +#: ../src/roster_window.py:3132 #, python-format msgid "" "By removing these contacts:%s\n" @@ -9270,20 +9159,19 @@ "también estás eliminando las autorizaciones resultantes y ellos siempre te " "verán desconectado."
-#: ../src/roster_window.py:3191 +#: ../src/roster_window.py:3171 msgid "No account available" msgstr "Cuenta no disponible"
-#: ../src/roster_window.py:3192 +#: ../src/roster_window.py:3172 msgid "You must create an account before you can chat with other contacts." msgstr "Debes crear una cuenta antes de poder conversar con otros contactos."
-#: ../src/roster_window.py:3708 +#: ../src/roster_window.py:3687 msgid "Metacontacts storage not supported by your server" msgstr "Tu servidor no soporta almacenamiento de metacontactos"
-#: ../src/roster_window.py:3710 -#, fuzzy +#: ../src/roster_window.py:3689 msgid "" "Your server does not support storing metacontacts information. So those " "information will not be saved on next reconnection." @@ -9291,195 +9179,184 @@ "Tu servidor no soporta almacenar información de metacontactos. Luego toda la " "información referente no se guardará en la próxima reconexión."
-#: ../src/roster_window.py:3784 +#: ../src/roster_window.py:3763 msgid "" "You are about to create a metacontact. Are you sure you want to continue?" msgstr "Vas a crear un metacontacto. ¿Estás seguro de continuar?"
-#: ../src/roster_window.py:3786 -#, fuzzy +#: ../src/roster_window.py:3765 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 " "accounts." msgstr "" -"Metacontactos son una forma de reagrupar varios contactos en una línea. " +"Los metacontactos son una forma de reagrupar varios contactos en una línea. " "Generalmente se usan cuando la misma persona tiene varias cuentas de Jabber " "o cuentas de transportes."
-#: ../src/roster_window.py:3899 -#, fuzzy +#: ../src/roster_window.py:3878 msgid "Invalid file URI:" -msgstr "Archivo no válido" +msgstr "URI de archivo no válido:"
-#: ../src/roster_window.py:3910 -#, fuzzy, python-format +#: ../src/roster_window.py:3889 +#, python-format msgid "Do you want to send this file to %s:" msgid_plural "Do you want to send these files to %s:" -msgstr[0] "%s quiere enviarte un archivo:" -msgstr[1] "%s quiere enviarte un archivo:" +msgstr[0] "Quieres enviar este archivo a %s:" +msgstr[1] "Quieres enviar estos archivos a %s:"
#. new chat #. single message #. for chat_with #. for single message #. join gc -#: ../src/roster_window.py:4587 ../src/roster_window.py:4658 -#: ../src/roster_window.py:4667 ../src/systray.py:243 ../src/systray.py:254 -#: ../src/systray.py:266 +#: ../src/roster_window.py:4566 ../src/roster_window.py:4637 +#: ../src/roster_window.py:4646 ../src/systray.py:219 ../src/systray.py:224 +#: ../src/systray.py:230 #, python-format msgid "using account %s" msgstr "usando la cuenta %s"
#. add -#: ../src/roster_window.py:4674 +#: ../src/roster_window.py:4653 #, python-format msgid "to %s account" msgstr "a la cuenta %s"
#. disco -#: ../src/roster_window.py:4679 +#: ../src/roster_window.py:4658 #, python-format msgid "using %s account" msgstr "usando la cuenta %s"
-#: ../src/roster_window.py:4716 ../src/systray.py:303 +#: ../src/roster_window.py:4695 ../src/systray.py:257 msgid "_Manage Bookmarks..." msgstr "Gestionar _marcadores..."
#. profile, avatar -#: ../src/roster_window.py:4736 +#: ../src/roster_window.py:4715 #, python-format msgid "of account %s" msgstr "de la cuenta %s"
-#: ../src/roster_window.py:4776 +#: ../src/roster_window.py:4755 #, python-format msgid "for account %s" msgstr "para la cuenta %s"
-#: ../src/roster_window.py:4838 ../src/roster_window.py:4947 +#: ../src/roster_window.py:4817 ../src/roster_window.py:4926 msgid "_Change Status Message" msgstr "_Cambiar mensaje de estado"
-#: ../src/roster_window.py:4865 -#, fuzzy +#: ../src/roster_window.py:4844 msgid "Publish Tune" -msgstr "_Publicar" +msgstr "Hora de publicación"
-#: ../src/roster_window.py:4873 +#: ../src/roster_window.py:4851 msgid "Mood" -msgstr "" +msgstr "Estado de ánimo"
-#: ../src/roster_window.py:4879 -#, fuzzy +#: ../src/roster_window.py:4855 msgid "Activity" -msgstr "Activo" +msgstr "Actividad"
-#: ../src/roster_window.py:4884 -#, fuzzy +#: ../src/roster_window.py:4860 msgid "Configure Services..." -msgstr "_Descubrir servicios..." +msgstr "Configurar servicios..."
-#: ../src/roster_window.py:5035 +#: ../src/roster_window.py:5014 msgid "_Maximize All" -msgstr "" +msgstr "_Maximizar todo"
#. Send Group Message -#: ../src/roster_window.py:5043 ../src/roster_window.py:5568 +#: ../src/roster_window.py:5022 ../src/roster_window.py:5548 msgid "Send Group M_essage" msgstr "Enviar mensaje a grupo"
-#: ../src/roster_window.py:5051 +#: ../src/roster_window.py:5030 msgid "To all users" msgstr "A todos los usuarios"
-#: ../src/roster_window.py:5055 +#: ../src/roster_window.py:5034 msgid "To all online users" msgstr "A todos los usuarios en línea"
-#: ../src/roster_window.py:5465 +#: ../src/roster_window.py:5445 msgid "I would like to add you to my roster" msgstr "Me gustaría añadirte a mi lista de contactos"
#. Manage Transport submenu -#: ../src/roster_window.py:5588 -#, fuzzy +#: ../src/roster_window.py:5568 msgid "_Manage Contacts" -msgstr "Renombrar contacto" +msgstr "_Gestionar contactos"
#. Send single message -#: ../src/roster_window.py:5649 -#, fuzzy +#: ../src/roster_window.py:5629 msgid "Send Single Message" -msgstr "_Enviar mensaje" +msgstr "Enviar mensaje"
#. Manage Transport submenu -#: ../src/roster_window.py:5705 -#, fuzzy +#: ../src/roster_window.py:5685 msgid "_Manage Transport" -msgstr "Transportes" +msgstr "_Gestionar transporte"
#. Modify Transport -#: ../src/roster_window.py:5713 -#, fuzzy +#: ../src/roster_window.py:5693 msgid "_Modify Transport" -msgstr "Mostrar Trans_portes" +msgstr "_Modificar transporte"
-#: ../src/roster_window.py:5788 +#: ../src/roster_window.py:5767 msgid "_Maximize" -msgstr "" +msgstr "_Maximizar"
-#: ../src/roster_window.py:5795 -#, fuzzy +#: ../src/roster_window.py:5774 msgid "_Disconnect" -msgstr "Contacto desconectado" +msgstr "_Desconectar"
-#: ../src/roster_window.py:5875 -#, fuzzy +#: ../src/roster_window.py:5854 msgid "_New Group Chat" -msgstr "Nuevo grupo de charla" +msgstr "_Nuevo grupo de charla"
#. History manager -#: ../src/roster_window.py:5988 +#: ../src/roster_window.py:5967 msgid "History Manager" msgstr "Histórico"
-#: ../src/roster_window.py:5997 ../src/systray.py:341 +#: ../src/roster_window.py:5976 msgid "_Join New Group Chat" msgstr "_Entrar en nuevo grupo de charla"
-#: ../src/roster_window.py:6207 +#: ../src/roster_window.py:6186 msgid "Change Status Message..." msgstr "Cambiar mensaje de estado..."
#: ../src/search_window.py:93 msgid "Waiting for results" -msgstr "" +msgstr "Esperando resultados"
#: ../src/search_window.py:133 ../src/search_window.py:211 msgid "Error in received dataform" -msgstr "" +msgstr "Error en la información recibida del formulario"
#. No result #: ../src/search_window.py:167 ../src/search_window.py:203 msgid "No result" -msgstr "" +msgstr "Sin resultados"
-#: ../src/session.py:124 +#: ../src/session.py:123 msgid "Disk WriteError" -msgstr "" +msgstr "Error de escritura en disco"
-#: ../src/session.py:238 +#: ../src/session.py:237 #, python-format msgid "Subject: %s" msgstr "Tema: %s"
-#: ../src/session.py:398 ../src/session.py:433 +#: ../src/session.py:397 ../src/session.py:432 msgid "Confirm these session options" -msgstr "" +msgstr "Confirma estas opciones de la sesión"
-#: ../src/session.py:400 +#: ../src/session.py:399 #, python-format msgid "" "The remote client wants to negotiate an session with these features:\n" @@ -9488,8 +9365,13 @@ "\n" "\tAre these options acceptable?" msgstr "" +"El cliente remoto quiere negociar una sesión con estas características:\n" +"\n" +"\t%s\n" +"\n" +"\t¿Son aceptables estas opciones?"
-#: ../src/session.py:434 +#: ../src/session.py:433 #, python-format msgid "" "The remote client selected these options:\n" @@ -9498,12 +9380,17 @@ "\n" "Continue with the session?" msgstr "" +"El cliente remoto seleccionó estas opciones:\n" +"\n" +"%s\n" +"\n" +"¿Continuar con la sesión?"
-#: ../src/systray.py:188 +#: ../src/systray.py:176 msgid "_Change Status Message..." msgstr "_Cambiar mensaje de estado..."
-#: ../src/systray.py:322 +#: ../src/systray.py:270 msgid "Hide this menu" msgstr "Oculta este menú"
@@ -9522,11 +9409,11 @@
#: ../src/tooltips.py:432 msgid " [blocked]" -msgstr "" +msgstr " [bloqueado]"
#: ../src/tooltips.py:436 msgid " [minimized]" -msgstr "" +msgstr " [minimizado]"
#: ../src/tooltips.py:451 ../src/tooltips.py:706 msgid "Status: " @@ -9543,14 +9430,12 @@ msgstr " desde %s"
#: ../src/tooltips.py:501 -#, fuzzy msgid "Connected" -msgstr "Conexión" +msgstr "Conectado"
#: ../src/tooltips.py:503 -#, fuzzy msgid "Disconnected" -msgstr "Contacto desconectado" +msgstr "Desconectado"
#. ('both' is the normal sub so we don't show it) #: ../src/tooltips.py:532 @@ -9562,19 +9447,16 @@ msgstr "OpenPGP: "
#: ../src/tooltips.py:587 -#, fuzzy msgid "Mood:" -msgstr "Salón:" +msgstr "Estado de ánimo:"
#: ../src/tooltips.py:602 -#, fuzzy msgid "Activity:" -msgstr "Activo" +msgstr "Actividad:"
#: ../src/tooltips.py:638 -#, fuzzy msgid "Tune:" -msgstr "Tipo:" +msgstr "Música:"
#: ../src/tooltips.py:664 msgid "Download" @@ -9643,9 +9525,8 @@ msgstr "desde %s"
#: ../src/vcard.py:311 -#, fuzzy msgid "<b>Affiliation:</b>" -msgstr "<b>Programas</b>" +msgstr "<b>Afiliación:</b>"
#: ../src/vcard.py:319 msgid "" @@ -9685,12 +9566,27 @@
#: ../src/vcard.py:334 msgid "There is no pending subscription request." -msgstr "" +msgstr "No hay ninguna suscripción pendiente."
#: ../src/vcard.py:339 ../src/vcard.py:377 ../src/vcard.py:507 msgid " resource with priority " msgstr " recurso con prioridad "
+#~ msgid "_Host:" +#~ msgstr "_Servidor" + +#~ msgid "Requires compilation of the idle module from Gajim sources." +#~ msgstr "" +#~ "Requiere la compilación del módulo de inactividad en las fuentes de Gajim." + +#~ msgid "Click to see features (like MSN, ICQ transports) of jabber servers" +#~ msgstr "" +#~ "Click para ver características (como transportes de MSN e ICQ) de los " +#~ "servidores de Jabber" + +#~ msgid "Servers Features" +#~ msgstr "Características de los servidores" + #~ msgid "OpenPGP Encryption" #~ msgstr "Encriptación OpenPGP"
@@ -9720,9 +9616,6 @@ #~ msgid "_Retype Password:" #~ msgstr "_Reintroduce la contraseña:"
-#~ msgid "_Use proxy" -#~ msgstr "_Usar proxy" - #~ msgid "" #~ "If checked, all local contacts that use a Bonjour compatible chat client " #~ "(like iChat, Trillian or Gaim) will be shown in roster. You don't need to " @@ -9908,9 +9801,6 @@ #~ msgid "_Before time:" #~ msgstr "_Antes de la hora"
-#~ msgid "_Font:" -#~ msgstr "_Fuente:" - #~ msgid "_Never" #~ msgstr "_Nunca"
@@ -10477,9 +10367,6 @@ #~ "Puedes definir opciones avanzadas usando "Editar->Cuentas" desde " #~ "laventana principal."
-#~ msgid "Service" -#~ msgstr "Servicio" - #~ msgid "" #~ "Your new account has been created and added to your gajim configuration.\n" #~ "You can set advanced account options using "Edit->Accounts" in the main "
Modified: branches/osx_newbuildsys/setup_osx.py =================================================================== --- branches/osx_newbuildsys/setup_osx.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/setup_osx.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -35,7 +35,7 @@
GTK_DIR="/Library/Frameworks/GTK+.framework/Versions/Current" NAME = 'Gajim' -VERSION = '0.12-beta1' +VERSION = '0.12.1' DESCRIPTION = 'A full featured Jabber client' AUTHOR = 'Gajim Development Team' URL = 'http://www.gajim.org/'
Modified: branches/osx_newbuildsys/setup_win32.py =================================================================== --- branches/osx_newbuildsys/setup_win32.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/setup_win32.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -71,7 +71,7 @@
setup( name = 'Gajim', - version = '0.12-beta1', + version = '0.12.1', description = 'A full featured Jabber client', author = 'Gajim Development Team', url = 'http://www.gajim.org/',
Modified: branches/osx_newbuildsys/src/chat_control.py =================================================================== --- branches/osx_newbuildsys/src/chat_control.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/chat_control.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -429,9 +429,9 @@ def _conv_textview_key_press_event(self, widget, event): if gtk.gtk_version < (2, 12, 0): return - if event.state & gtk.gdk.CONTROL_MASK and event.keyval == gtk.keysyms.c \ - or event.state & gtk.gdk.SHIFT_MASK and event.keyval in ( - gtk.keysyms.Page_Down, gtk.keysyms.Page_Up): + if (event.state & gtk.gdk.CONTROL_MASK and event.keyval in (gtk.keysyms.c, + gtk.keysyms.Insert)) or (event.state & gtk.gdk.SHIFT_MASK and \ + event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.Page_Up)): return False self.parent_win.notebook.emit('key_press_event', event) return True @@ -1059,6 +1059,8 @@ self.msg_textview.set_sensitive(False) self.msg_textview.set_editable(False) self.conv_textview.tv.grab_focus() + + self.no_autonegotiation = False # FIXME: Set sensitivity for toolbar
################################################################################
Modified: branches/osx_newbuildsys/src/common/Makefile =================================================================== --- branches/osx_newbuildsys/src/common/Makefile 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/Makefile 2008-12-23 23:42:49 UTC (rev 10895) @@ -27,12 +27,12 @@ i18n.py \ idle.py \ logger.py \ - nslookup.py \ optparser.py \ passwords.py \ pep.py \ proxy65_manager.py \ pubsub.py \ + resolver.py \ rst_xhtml_generator.py \ sleepy.py \ socks5.py \
Modified: branches/osx_newbuildsys/src/common/config.py =================================================================== --- branches/osx_newbuildsys/src/common/config.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/config.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -313,7 +313,7 @@ 'keep_alives_enabled': [ opt_bool, True], # send keepalive every N seconds of inactivity 'keep_alive_every_foo_secs': [ opt_int, 55 ], - 'time_for_keep_alive_answer': [ opt_int, 20, _('How many seconds to wait for the answer of keepalive packet before we try to reconnect.') ], + 'time_for_keep_alive_answer': [ opt_int, 30, _('How many seconds to wait for the answer of keepalive packet before we try to reconnect.') ], # try for 2 minutes before giving up (aka. timeout after those seconds) 'try_connecting_for_foo_secs': [ opt_int, 60 ], 'http_auth': [opt_str, 'ask'], # yes, no, ask @@ -358,8 +358,17 @@ 'type': [ opt_str, 'http' ], 'host': [ opt_str, '' ], 'port': [ opt_int, 3128 ], + 'useauth': [ opt_bool, False ], 'user': [ opt_str, '' ], 'pass': [ opt_str, '' ], + 'bosh_uri': [ opt_str, '' ], + 'bosh_port': [ opt_int, 80 ], + 'bosh_useproxy': [ opt_bool, False ], + 'bosh_wait': [ opt_int, 30 ], + 'bosh_hold': [ opt_int, 2 ], + 'bosh_content': [ opt_str, 'text/xml; charset=utf-8' ], + 'bosh_http_pipelining': [ opt_bool, False ], + 'bosh_wait_for_restart_response': [ opt_bool, False ], }, {}), 'themes': ({ 'accounttextcolor': [ opt_color, 'black', '', True ],
Modified: branches/osx_newbuildsys/src/common/connection.py =================================================================== --- branches/osx_newbuildsys/src/common/connection.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/connection.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -219,7 +219,7 @@
def _disconnectedReconnCB(self): '''Called when we are disconnected''' - log.debug('disconnectedReconnCB') + log.info('disconnectedReconnCB called') if gajim.account_is_connected(self.name): # we cannot change our status to offline or connecting # after we auth to server @@ -374,9 +374,9 @@ # 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): @@ -407,7 +407,7 @@
def connect(self, data = None): ''' Start a connection to the Jabber server. - Returns connection, and connection type ('tls', 'ssl', 'tcp', '') + Returns connection, and connection type ('tls', 'ssl', 'plain', '') data MUST contain hostname, usessl, proxy, use_custom_host, custom_host (if use_custom_host), custom_port (if use_custom_host)''' if self.connection: @@ -437,11 +437,11 @@ # create connection if it doesn't already exist self.connected = 1 if p and p in gajim.config.get_per('proxies'): - proxy = {'host': gajim.config.get_per('proxies', p, 'host')} - proxy['port'] = gajim.config.get_per('proxies', p, 'port') - proxy['user'] = gajim.config.get_per('proxies', p, 'user') - proxy['password'] = gajim.config.get_per('proxies', p, 'pass') - proxy['type'] = gajim.config.get_per('proxies', p, 'type') + proxy = {} + proxyptr = gajim.config.get_per('proxies', p) + for key in proxyptr.keys(): + proxy[key] = proxyptr[key][1] + elif gajim.config.get_per('accounts', self.name, 'use_env_http_proxy'): try: try: @@ -477,7 +477,6 @@ proxy = None else: proxy = None - h = hostname p = 5222 ssl_p = 5223 @@ -513,67 +512,13 @@ i['ssl_port'] = ssl_p self._connect_to_next_host()
- def on_proxy_failure(self, reason): - log.debug('Connection to proxy failed') - self.time_to_reconnect = None - self.on_connect_failure = None - self.disconnect(on_purpose = True) - self.dispatch('STATUS', 'offline') - self.dispatch('CONNECTION_LOST', - (_('Connection to proxy failed'), reason))
- def _connect_to_next_type(self, retry=False): - log.debug('Connection to next type') - if len(self._connection_types): - self._current_type = self._connection_types.pop(0) - if self.last_connection: - self.last_connection.socket.disconnect() - self.last_connection = None - self.connection = None - if gajim.verbose: - if log.getEffectiveLevel() == logging.DEBUG: - d = ['always'] - else: - d = ['always', 'nodebuilder'] - con = common.xmpp.NonBlockingClient(self._hostname, debug = d, - caller = self, on_connect = self.on_connect_success, - on_proxy_failure = self.on_proxy_failure, - on_connect_failure = self._connect_to_next_type) - else: - con = common.xmpp.NonBlockingClient(self._hostname, debug = [], - caller = self, on_connect = self.on_connect_success, - on_proxy_failure = self.on_proxy_failure, - on_connect_failure = self._connect_to_next_type) - self.last_connection = con - # increase default timeout for server responses - common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs - con.set_idlequeue(gajim.idlequeue) - # FIXME: this is a hack; need a better way - if self.on_connect_success == self._on_new_account: - con.RegisterDisconnectHandler(self._on_new_account) - - if self._current_type == 'ssl': - port = self._current_host['ssl_port'] - secur = 1 - else: - port = self._current_host['port'] - if self._current_type == 'plain': - secur = 0 - else: - secur = None - log.info('Connecting to %s: [%s:%d]', self.name, - self._current_host['host'], port) - con.connect((self._current_host['host'], port), proxy=self._proxy, - secure = secur) - else: - self._connect_to_next_host(retry) - def _connect_to_next_host(self, retry = False): log.debug('Connection to next host') if len(self._hosts): # No config option exist when creating a new account if self.last_connection_type: - if self.last_connection_type == 'tcp': + if self.last_connection_type == 'plain': self._connection_types = ['plain'] else: self._connection_types = [self.last_connection_type] @@ -582,10 +527,20 @@ 'connection_types').split() else: self._connection_types = ['tls', 'ssl', 'plain'] + + if self._proxy and self._proxy['type']=='bosh': + # with BOSH, we can't do TLS negotiation with <starttls>, we do only "plain" + # connection and TLS with handshake right after TCP connecting ("ssl") + scheme = common.xmpp.transports_nb.urisplit(self._proxy['bosh_uri'])[0] + if scheme=='https': + self._connection_types = ['ssl'] + else: + self._connection_types = ['plain'] + host = self._select_next_host(self._hosts) self._current_host = host self._hosts.remove(host) - self._connect_to_next_type() + self.connect_to_next_type()
else: if not retry and self.retrycount == 0: @@ -601,6 +556,69 @@ # try reconnect if connection has failed before auth to server self._disconnectedReconnCB()
+ def connect_to_next_type(self, retry=False): + if len(self._connection_types): + self._current_type = self._connection_types.pop(0) + if self.last_connection: + self.last_connection.socket.disconnect() + self.last_connection = None + self.connection = None + + if self._current_type == 'ssl': + # SSL (force TLS on different port than plain) + # If we do TLS over BOSH, port of XMPP server should be the standard one + # and TLS should be negotiated because TLS on 5223 is deprecated + if self._proxy and self._proxy['type']=='bosh': + port = self._current_host['port'] + else: + port = self._current_host['ssl_port'] + elif self._current_type == 'tls': + # TLS - negotiate tls after XMPP stream is estabilished + port = self._current_host['port'] + elif self._current_type == 'plain': + # plain connection on defined port + port = self._current_host['port'] + + cacerts = os.path.join(common.gajim.DATA_DIR, 'other', 'cacerts.pem') + mycerts = common.gajim.MY_CACERTS + secure_tuple = (self._current_type, cacerts, mycerts) + + con = common.xmpp.NonBlockingClient( + domain=self._hostname, + caller=self, + idlequeue=gajim.idlequeue) + + self.last_connection = con + # increase default timeout for server responses + common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs + # FIXME: this is a hack; need a better way + if self.on_connect_success == self._on_new_account: + con.RegisterDisconnectHandler(self._on_new_account) + + self.log_hosttype_info(port) + con.connect( + hostname=self._current_host['host'], + port=port, + on_connect=self.on_connect_success, + on_proxy_failure=self.on_proxy_failure, + on_connect_failure=self.connect_to_next_type, + proxy=self._proxy, + secure_tuple = secure_tuple) + else: + self._connect_to_next_host(retry) + + def log_hosttype_info(self, port): + msg = '>>>>>> Connecting to %s [%s:%d], type = %s' % (self.name, + self._current_host['host'], port, self._current_type) + log.info(msg) + if self._proxy: + msg = '>>>>>> ' + if self._proxy['type']=='bosh': + msg = '%s over BOSH %s:%s' % (msg, self._proxy['bosh_uri'], self._proxy['bosh_port']) + if self._proxy['type'] in ['http','socks5'] or self._proxy['bosh_useproxy']: + msg = '%s over proxy %s:%s' % (msg, self._proxy['host'], self._proxy['port']) + log.info(msg) + def _connect_failure(self, con_type = None): if not con_type: # we are not retrying, and not conecting @@ -611,16 +629,24 @@ (_('Could not connect to "%s"') % self._hostname, _('Check your connection or try again later.')))
+ def on_proxy_failure(self, reason): + log.error('Connection to proxy failed: %s' % reason) + self.time_to_reconnect = None + self.on_connect_failure = None + self.disconnect(on_purpose = True) + self.dispatch('STATUS', 'offline') + self.dispatch('CONNECTION_LOST', + (_('Connection to proxy failed'), reason)) + def _connect_success(self, con, con_type): if not self.connected: # We went offline during connecting process # FIXME - not possible, maybe it was when we used threads return _con_type = con_type - # xmpp returns 'tcp', but we set 'plain' in connection_types in config - if _con_type == 'tcp': - _con_type = 'plain' if _con_type != self._current_type: - self._connect_to_next_type() + log.info('Connecting to next type beacuse desired is %s and returned is %s' + % (self._current_type, _con_type)) + self.connect_to_next_type() return con.RegisterDisconnectHandler(self._on_disconnected) if _con_type == 'plain' and gajim.config.get_per('accounts', self.name, @@ -682,7 +708,12 @@ (con.Connection.ssl_fingerprint_sha1,)) return True self._register_handlers(con, con_type) - con.auth(name, self.password, self.server_resource, 1, self.__on_auth) + con.auth( + user=name, + password=self.password, + resource=self.server_resource, + sasl=1, + on_auth=self.__on_auth)
def ssl_certificate_accepted(self): if not self.connection: @@ -1019,7 +1050,11 @@ p.setStatus(msg) self.remove_all_transfers() self.time_to_reconnect = None - self.connection.start_disconnect(p, self._on_disconnected) + + self.connection.RegisterDisconnectHandler(self._on_disconnected) + self.connection.send(p, now=True) + self.connection.start_disconnect() + #self.connection.start_disconnect(p, self._on_disconnected) else: self.time_to_reconnect = None self._on_disconnected() @@ -1054,7 +1089,7 @@ def _on_disconnected(self): ''' called when a disconnect request has completed successfully''' self.dispatch('STATUS', 'offline') - self.disconnect() + self.disconnect(on_purpose=True)
def get_status(self): return STATUS_LIST[self.connected] @@ -1072,8 +1107,16 @@ chatstate=None, msg_id=None, composing_xep=None, resource=None, user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None, original_message=None, delayed=None): - if not self.connection: + if not self.connection or self.connected < 2: return 1 + try: + jid = helpers.parse_jid(jid) + except helpers.InvalidFormat: + self.dispatch('ERROR', (_('Invalid Jabber ID'), + _('It is not possible to send a message to %s, this JID is not ' + 'valid.') % jid)) + return + if msg and not xhtml and gajim.config.get( 'rst_formatting_outgoing_messages'): from common.rst_xhtml_generator import create_xhtml @@ -1605,7 +1648,7 @@ 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) + 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
Modified: branches/osx_newbuildsys/src/common/connection_handlers.py =================================================================== --- branches/osx_newbuildsys/src/common/connection_handlers.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/connection_handlers.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -214,7 +214,7 @@ iq.setID(file_props['request-id']) query = iq.setTag('query') query.setNamespace(common.xmpp.NS_BYTESTREAM) - query.setAttr('mode', 'tcp') + query.setAttr('mode', 'plain') query.setAttr('sid', file_props['sid']) for ft_host in ft_add_hosts: # The streamhost, if set @@ -1294,11 +1294,11 @@ except KeyError: return None
- def terminate_sessions(self): + def terminate_sessions(self, send_termination=False): '''send termination messages and delete all active sessions''' for jid in self.sessions: for thread_id in self.sessions[jid]: - self.sessions[jid][thread_id].terminate() + self.sessions[jid][thread_id].terminate(send_termination)
self.sessions = {}
@@ -1682,8 +1682,12 @@ self._HttpAuthCB(con, msg) return
- frm = helpers.get_full_jid_from_iq(msg) - jid = helpers.get_jid_from_iq(msg) + try: + frm = helpers.get_full_jid_from_iq(msg) + jid = helpers.get_jid_from_iq(msg) + except helpers.InvalidFormat: + self.dispatch('ERROR', (_('Invalid Jabber ID'), + _('A message from a non-valid JID arrived, it has been ignored.')))
addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS)
@@ -2007,7 +2011,7 @@
status = prs.getStatus() or '' show = prs.getShow() - if not show in ['away', 'chat', 'dnd', 'xa']: + if not show in STATUS_LIST: show = '' # We ignore unknown show if not ptype and not show: show = 'online'
Deleted: branches/osx_newbuildsys/src/common/nslookup.py =================================================================== --- branches/osx_newbuildsys/src/common/nslookup.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/nslookup.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,348 +0,0 @@ -# -*- coding:utf-8 -*- -## src/common/nslookup.py -## -## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com> -## Nikos Kouremenos <kourem AT gmail.com> -## Copyright (C) 2007 Yann Leboulanger <asterix AT lagaule.org> -## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org> -## -## 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 sys -import os -import re -from common import helpers - -from xmpp.idlequeue import * - -if os.name == 'nt': - from subprocess import * # python24 only. we ask this for Windows -elif os.name == 'posix': - import fcntl - -# it is good to check validity of arguments, when calling system commands -ns_type_pattern = re.compile('^[a-z]+$') - -# match srv host_name -host_pattern = re.compile('^[a-z0-9-._]*[a-z0-9].[a-z]{2,}$') - -class Resolver: - def __init__(self, idlequeue): - self.idlequeue = idlequeue - # dict {host : list of srv records} - self.resolved_hosts = {} - # dict {host : list of callbacks} - self.handlers = {} - - def parse_srv_result(self, fqdn, result): - ''' parse the output of nslookup command and return list of - properties: 'host', 'port','weight', 'priority' corresponding to the found - srv hosts ''' - if os.name == 'nt': - return self._parse_srv_result_nt(fqdn, result) - elif os.name == 'posix': - return self._parse_srv_result_posix(fqdn, result) - - def _parse_srv_result_nt(self, fqdn, result): - # output from win32 nslookup command - if not result: - return [] - hosts = [] - lines = result.replace('\r','').split('\n') - current_host = None - for line in lines: - line = line.lstrip() - if line == '': - continue - if line.startswith(fqdn): - rest = line[len(fqdn):] - if rest.find('service') > -1: - current_host = {} - elif isinstance(current_host, dict): - res = line.strip().split('=') - if len(res) != 2: - if len(current_host) == 4: - hosts.append(current_host) - current_host = None - continue - prop_type = res[0].strip() - prop_value = res[1].strip() - if prop_type.find('prio') > -1: - try: - current_host['prio'] = int(prop_value) - except ValueError: - continue - elif prop_type.find('weight') > -1: - try: - current_host['weight'] = int(prop_value) - except ValueError: - continue - elif prop_type.find('port') > -1: - try: - current_host['port'] = int(prop_value) - except ValueError: - continue - elif prop_type.find('host') > -1: - # strip '.' at the end of hostname - if prop_value[-1] == '.': - prop_value = prop_value[:-1] - current_host['host'] = prop_value - if len(current_host) == 4: - hosts.append(current_host) - current_host = None - return hosts - - def _parse_srv_result_posix(self, fqdn, result): - # typical output of bind-tools nslookup command: - # _xmpp-client._tcp.jabber.org service = 30 30 5222 jabber.org. - if not result: - return [] - ufqdn = helpers.ascii_to_idn(fqdn) # Unicode domain name - hosts = [] - lines = result.split('\n') - for line in lines: - if line == '': - continue - domain = None - if line.startswith(fqdn): - domain = fqdn - elif helpers.decode_string(line).startswith(ufqdn): - line = helpers.decode_string(line) - domain = ufqdn - if domain: - rest = line[len(domain):].split('=') - if len(rest) != 2: - continue - answer_type, props_str = rest - if answer_type.strip() != 'service': - continue - props = props_str.strip().split(' ') - if len(props) < 4: - continue - prio, weight, port, host = props[-4:] - if host[-1] == '.': - host = host[:-1] - try: - prio = int(prio) - weight = int(weight) - port = int(port) - except ValueError: - continue - hosts.append({'host': host, 'port': port,'weight': weight, - 'prio': prio}) - return hosts - - def _on_ready(self, host, result): - # nslookup finished, parse the result and call the handlers - result_list = self.parse_srv_result(host, result) - - # practically it is impossible to be the opposite, but who knows :) - if host not in self.resolved_hosts: - self.resolved_hosts[host] = result_list - if host in self.handlers: - for callback in self.handlers[host]: - callback(host, result_list) - del(self.handlers[host]) - - def start_resolve(self, host): - ''' spawn new nslookup process and start waiting for results ''' - ns = NsLookup(self._on_ready, host) - ns.set_idlequeue(self.idlequeue) - ns.commandtimeout = 10 - ns.start() - - def resolve(self, host, on_ready): - if not host: - # empty host, return empty list of srv records - on_ready([]) - return - if host in self.resolved_hosts: - # host is already resolved, return cached values - on_ready(host, self.resolved_hosts[host]) - return - if host in self.handlers: - # host is about to be resolved by another connection, - # attach our callback - self.handlers[host].append(on_ready) - else: - # host has never been resolved, start now - self.handlers[host] = [on_ready] - self.start_resolve(host) - -# TODO: move IdleCommand class in other file, maybe helpers ? -class IdleCommand(IdleObject): - def __init__(self, on_result): - # how long (sec.) to wait for result ( 0 - forever ) - # it is a class var, instead of a constant and we can override it. - self.commandtimeout = 0 - # when we have some kind of result (valid, ot not) we call this handler - self.result_handler = on_result - # if it is True, we can safetely execute the command - self.canexecute = True - self.idlequeue = None - self.result = '' - - def set_idlequeue(self, idlequeue): - self.idlequeue = idlequeue - - def _return_result(self): - if self.result_handler: - self.result_handler(self.result) - self.result_handler = None - - def _compose_command_args(self): - return ['echo', 'da'] - - def _compose_command_line(self): - ''' return one line representation of command and its arguments ''' - return ' '.join(self._compose_command_args()) - - def wait_child(self): - if self.pipe.poll() is None: - # result timeout - if self.endtime < self.idlequeue.current_time(): - self._return_result() - self.pipe.stdout.close() - self.pipe.stdin.close() - else: - # child is still active, continue to wait - self.idlequeue.set_alarm(self.wait_child, 0.1) - else: - # child has quit - self.result = self.pipe.stdout.read() - self._return_result() - self.pipe.stdout.close() - self.pipe.stdin.close() - def start(self): - if not self.canexecute: - self.result = '' - self._return_result() - return - if os.name == 'nt': - self._start_nt() - elif os.name == 'posix': - self._start_posix() - - def _start_nt(self): - # if gajim is started from noninteraactive shells stdin is closed and - # cannot be forwarded, so we have to keep it open - self.pipe = Popen(self._compose_command_args(), stdout=PIPE, - bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE) - if self.commandtimeout >= 0: - self.endtime = self.idlequeue.current_time() + self.commandtimeout - self.idlequeue.set_alarm(self.wait_child, 0.1) - - def _start_posix(self): - self.pipe = os.popen(self._compose_command_line()) - self.fd = self.pipe.fileno() - fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK) - self.idlequeue.plug_idle(self, False, True) - if self.commandtimeout >= 0: - self.idlequeue.set_read_timeout(self.fd, self.commandtimeout) - - def end(self): - self.idlequeue.unplug_idle(self.fd) - try: - self.pipe.close() - except Exception: - pass - - def pollend(self): - self.idlequeue.remove_timeout(self.fd) - self.end() - self._return_result() - - def pollin(self): - try: - res = self.pipe.read() - except Exception: - res = '' - if res == '': - return self.pollend() - else: - self.result += res - - def read_timeout(self): - self.end() - self._return_result() - -class NsLookup(IdleCommand): - def __init__(self, on_result, host='_xmpp-client', type_ = 'srv'): - IdleCommand.__init__(self, on_result) - self.commandtimeout = 10 - self.host = host.lower() - self.type = type_.lower() - if not host_pattern.match(self.host): - # invalid host name - print >> sys.stderr, 'Invalid host: %s' % self.host - self.canexecute = False - return - if not ns_type_pattern.match(self.type): - print >> sys.stderr, 'Invalid querytype: %s' % self.type - self.canexecute = False - return - - def _compose_command_args(self): - return ['nslookup', '-type=' + self.type , self.host] - - def _return_result(self): - if self.result_handler: - self.result_handler(self.host, self.result) - self.result_handler = None - -# below lines is on how to use API and assist in testing -if __name__ == '__main__': - if os.name == 'posix': - idlequeue = IdleQueue() - elif os.name == 'nt': - idlequeue = SelectIdleQueue() - # testing Resolver class - import gobject - import gtk - - resolver = Resolver(idlequeue) - - def clicked(widget): - host = text_view.get_text() - def on_result(host, result_array): - print 'Result:\n' + repr(result_array) - resolver.resolve(host, on_result) - win = gtk.Window() - win.set_border_width(6) - text_view = gtk.Entry() - text_view.set_text('_xmpp-client._tcp.jabber.org') - hbox = gtk.HBox() - hbox.set_spacing(3) - but = gtk.Button(' Lookup SRV ') - hbox.pack_start(text_view, 5) - hbox.pack_start(but, 0) - but.connect('clicked', clicked) - win.add(hbox) - win.show_all() - - def process(): - try: - idlequeue.process() - except Exception: - # Otherwise, an exception will stop our loop - gobject.timeout_add(200, process) - raise - return True - - gobject.timeout_add(200, process) - gtk.main() - -# vim: se ts=3:
Copied: branches/osx_newbuildsys/src/common/resolver.py (from rev 10894, trunk/src/common/resolver.py) =================================================================== --- branches/osx_newbuildsys/src/common/resolver.py (rev 0) +++ branches/osx_newbuildsys/src/common/resolver.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,428 @@ +## common/resolver.py +## +## Copyright (C) 2006 Dimitur Kirov dkirov@gmail.com +## +## 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 sys +import os +import re + +from xmpp.idlequeue import * + +# needed for nslookup +if os.name == 'nt': + from subprocess import * # python24 only. we ask this for Windows +elif os.name == 'posix': + import fcntl + +# it is good to check validity of arguments, when calling system commands +ns_type_pattern = re.compile('^[a-z]+$') + +# match srv host_name +host_pattern = re.compile('^[a-z0-9-._]*[a-z0-9].[a-z]{2,}$') + +USE_LIBASYNCNS = False + +try: + #raise ImportError("Manually disabled libasync") + import libasyncns + USE_LIBASYNCNS = True + log.info("libasyncns-python loaded") +except ImportError: + log.debug("Import of libasyncns-python failed, getaddrinfo will block", exc_info=True) + + # FIXME: Remove these prints before release, replace with a warning dialog. + print >> sys.stderr, "=" * 79 + print >> sys.stderr, "libasyncns-python not installed which means:" + print >> sys.stderr, " - nslookup will be used for SRV and TXT requests" + print >> sys.stderr, " - getaddrinfo will block" + print >> sys.stderr, "libasyncns-python can be found at https://launchpad.net/libasyncns-python" + print >> sys.stderr, "=" * 79 + + +def get_resolver(idlequeue): + if USE_LIBASYNCNS: + return LibAsyncNSResolver() + else: + return NSLookupResolver(idlequeue) + +class CommonResolver(): + def __init__(self): + # dict {"host+type" : list of records} + self.resolved_hosts = {} + # dict {"host+type" : list of callbacks} + self.handlers = {} + + def resolve(self, host, on_ready, type='srv'): + assert(type in ['srv', 'txt']) + if not host: + # empty host, return empty list of srv records + on_ready([]) + return + if self.resolved_hosts.has_key(host+type): + # host is already resolved, return cached values + on_ready(host, self.resolved_hosts[host+type]) + return + if self.handlers.has_key(host+type): + # host is about to be resolved by another connection, + # attach our callback + self.handlers[host+type].append(on_ready) + else: + # host has never been resolved, start now + self.handlers[host+type] = [on_ready] + self.start_resolve(host, type) + + def _on_ready(self, host, type, result_list): + # practically it is impossible to be the opposite, but who knows :) + if not self.resolved_hosts.has_key(host+type): + self.resolved_hosts[host+type] = result_list + if self.handlers.has_key(host+type): + for callback in self.handlers[host+type]: + callback(host, result_list) + del(self.handlers[host+type]) + + def start_resolve(self, host, type): + pass + + +class LibAsyncNSResolver(CommonResolver): + ''' + Asynchronous resolver using libasyncns-python. process() method has to be called + in order to proceed the pending requests. + Based on patch submitted by Damien Thebault. + ''' + def __init__(self): + self.asyncns = libasyncns.Asyncns() + CommonResolver.__init__(self) + + def start_resolve(self, host, type): + type = libasyncns.ns_t_srv + if type == 'txt': type = libasyncns.ns_t_txt + resq = self.asyncns.res_query(host, libasyncns.ns_c_in, type) + resq.userdata = {'host':host, 'type':type} + + # getaddrinfo to be done + #def resolve_name(self, dname, callback): + #resq = self.asyncns.getaddrinfo(dname) + #resq.userdata = {'callback':callback, 'dname':dname} + + def _on_ready(self, host, type, result_list): + if type == libasyncns.ns_t_srv: type = 'srv' + elif type == libasyncns.ns_t_txt: type = 'txt' + + CommonResolver._on_ready(self, host, type, result_list) + + + def process(self): + try: + self.asyncns.wait(False) + resq = self.asyncns.get_next() + except: + return True + if type(resq) == libasyncns.ResQuery: + # TXT or SRV result + while resq is not None: + try: + rl = resq.get_done() + except: + rl = [] + if rl: + for r in rl: + r['prio'] = r['pref'] + self._on_ready( + host = resq.userdata['host'], + type = resq.userdata['type'], + result_list = rl) + try: + resq = self.asyncns.get_next() + except: + resq = None + elif type(resq) == libasyncns.AddrInfoQuery: + # getaddrinfo result (A or AAAA) + rl = resq.get_done() + resq.userdata['callback'](resq.userdata['dname'], rl) + return True + +class NSLookupResolver(CommonResolver): + ''' + Asynchronous DNS resolver calling nslookup. Processing of pending requests + is invoked from idlequeue which is watching file descriptor of pipe of stdout + of nslookup process. + ''' + def __init__(self, idlequeue): + self.idlequeue = idlequeue + self.process = False + CommonResolver.__init__(self) + + def parse_srv_result(self, fqdn, result): + ''' parse the output of nslookup command and return list of + properties: 'host', 'port','weight', 'priority' corresponding to the found + srv hosts ''' + if os.name == 'nt': + return self._parse_srv_result_nt(fqdn, result) + elif os.name == 'posix': + return self._parse_srv_result_posix(fqdn, result) + + def _parse_srv_result_nt(self, fqdn, result): + # output from win32 nslookup command + if not result: + return [] + hosts = [] + lines = result.replace('\r','').split('\n') + current_host = None + for line in lines: + line = line.lstrip() + if line == '': + continue + if line.startswith(fqdn): + rest = line[len(fqdn):] + if rest.find('service') > -1: + current_host = {} + elif isinstance(current_host, dict): + res = line.strip().split('=') + if len(res) != 2: + if len(current_host) == 4: + hosts.append(current_host) + current_host = None + continue + prop_type = res[0].strip() + prop_value = res[1].strip() + if prop_type.find('prio') > -1: + try: + current_host['prio'] = int(prop_value) + except ValueError: + continue + elif prop_type.find('weight') > -1: + try: + current_host['weight'] = int(prop_value) + except ValueError: + continue + elif prop_type.find('port') > -1: + try: + current_host['port'] = int(prop_value) + except ValueError: + continue + elif prop_type.find('host') > -1: + # strip '.' at the end of hostname + if prop_value[-1] == '.': + prop_value = prop_value[:-1] + current_host['host'] = prop_value + if len(current_host) == 4: + hosts.append(current_host) + current_host = None + return hosts + + def _parse_srv_result_posix(self, fqdn, result): + # typical output of bind-tools nslookup command: + # _xmpp-client._tcp.jabber.org service = 30 30 5222 jabber.org. + if not result: + return [] + hosts = [] + lines = result.split('\n') + for line in lines: + if line == '': + continue + if line.startswith(fqdn): + rest = line[len(fqdn):].split('=') + if len(rest) != 2: + continue + answer_type, props_str = rest + if answer_type.strip() != 'service': + continue + props = props_str.strip().split(' ') + if len(props) < 4: + continue + prio, weight, port, host = props[-4:] + if host[-1] == '.': + host = host[:-1] + try: + prio = int(prio) + weight = int(weight) + port = int(port) + except ValueError: + continue + hosts.append({'host': host, 'port': port,'weight': weight, + 'prio': prio}) + return hosts + + def _on_ready(self, host, type, result): + # nslookup finished, parse the result and call the handlers + result_list = self.parse_srv_result(host, result) + CommonResolver._on_ready(self, host, type, result_list) + + + def start_resolve(self, host, type): + ''' spawn new nslookup process and start waiting for results ''' + ns = NsLookup(self._on_ready, host, type) + ns.set_idlequeue(self.idlequeue) + ns.commandtimeout = 10 + ns.start() + + +# TODO: move IdleCommand class in other file, maybe helpers ? +class IdleCommand(IdleObject): + def __init__(self, on_result): + # how long (sec.) to wait for result ( 0 - forever ) + # it is a class var, instead of a constant and we can override it. + self.commandtimeout = 0 + # when we have some kind of result (valid, ot not) we call this handler + self.result_handler = on_result + # if it is True, we can safetely execute the command + self.canexecute = True + self.idlequeue = None + self.result ='' + + def set_idlequeue(self, idlequeue): + self.idlequeue = idlequeue + + def _return_result(self): + if self.result_handler: + self.result_handler(self.result) + self.result_handler = None + + def _compose_command_args(self): + return ['echo', 'da'] + + def _compose_command_line(self): + ''' return one line representation of command and its arguments ''' + return reduce(lambda left, right: left + ' ' + right, self._compose_command_args()) + + def wait_child(self): + if self.pipe.poll() is None: + # result timeout + if self.endtime < self.idlequeue.current_time(): + self._return_result() + self.pipe.stdout.close() + self.pipe.stdin.close() + else: + # child is still active, continue to wait + self.idlequeue.set_alarm(self.wait_child, 0.1) + else: + # child has quit + self.result = self.pipe.stdout.read() + self._return_result() + self.pipe.stdout.close() + self.pipe.stdin.close() + def start(self): + if not self.canexecute: + self.result = '' + self._return_result() + return + if os.name == 'nt': + self._start_nt() + elif os.name == 'posix': + self._start_posix() + + def _start_nt(self): + # if gajim is started from noninteraactive shells stdin is closed and + # cannot be forwarded, so we have to keep it open + self.pipe = Popen(self._compose_command_args(), stdout=PIPE, + bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE) + if self.commandtimeout >= 0: + self.endtime = self.idlequeue.current_time() + self.commandtimeout + self.idlequeue.set_alarm(self.wait_child, 0.1) + + def _start_posix(self): + self.pipe = os.popen(self._compose_command_line()) + self.fd = self.pipe.fileno() + fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK) + self.idlequeue.plug_idle(self, False, True) + if self.commandtimeout >= 0: + self.idlequeue.set_read_timeout(self.fd, self.commandtimeout) + + def end(self): + self.idlequeue.unplug_idle(self.fd) + try: + self.pipe.close() + except: + pass + + def pollend(self): + self.idlequeue.remove_timeout(self.fd) + self.end() + self._return_result() + + def pollin(self): + try: + res = self.pipe.read() + except Exception, e: + res = '' + if res == '': + return self.pollend() + else: + self.result += res + + def read_timeout(self): + self.end() + self._return_result() + +class NsLookup(IdleCommand): + def __init__(self, on_result, host='_xmpp-client', type='srv'): + IdleCommand.__init__(self, on_result) + self.commandtimeout = 10 + self.host = host.lower() + self.type = type.lower() + if not host_pattern.match(self.host): + # invalid host name + print >> sys.stderr, 'Invalid host: %s' % self.host + self.canexecute = False + return + if not ns_type_pattern.match(self.type): + print >> sys.stderr, 'Invalid querytype: %s' % self.type + self.canexecute = False + return + + def _compose_command_args(self): + return ['nslookup', '-type=' + self.type , self.host] + + def _return_result(self): + if self.result_handler: + self.result_handler(self.host, self.type, self.result) + self.result_handler = None + +# below lines is on how to use API and assist in testing +if __name__ == '__main__': + if os.name == 'posix': + idlequeue = IdleQueue() + elif os.name == 'nt': + idlequeue = SelectIdleQueue() + # testing Resolver class + import gobject + import gtk + + resolver = Resolver(idlequeue) + + def clicked(widget): + global resolver + host = text_view.get_text() + def on_result(host, result_array): + print 'Result:\n' + repr(result_array) + resolver.resolve(host, on_result) + win = gtk.Window() + win.set_border_width(6) + text_view = gtk.Entry() + text_view.set_text('_xmpp-client._tcp.jabber.org') + hbox = gtk.HBox() + hbox.set_spacing(3) + but = gtk.Button(' Lookup SRV ') + hbox.pack_start(text_view, 5) + hbox.pack_start(but, 0) + but.connect('clicked', clicked) + win.add(hbox) + win.show_all() + gobject.timeout_add(200, idlequeue.process) + gtk.main()
Modified: branches/osx_newbuildsys/src/common/stanza_session.py =================================================================== --- branches/osx_newbuildsys/src/common/stanza_session.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/stanza_session.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -133,10 +133,10 @@ self.status = None self.negotiated = {}
- def terminate(self): + def terminate(self, send_termination = True): # only send termination message if we've sent a message and think they # have XEP-0201 support - if self.last_send > 0 and \ + if send_termination and self.last_send > 0 and \ (self.received_thread_id or self.last_receive == 0): msg = xmpp.Message() feature = msg.NT.feature
Modified: branches/osx_newbuildsys/src/common/xmpp/Makefile =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/Makefile 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/Makefile 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,24 +1,18 @@ -SRCS = __init__.py \ - auth.py \ - auth_nb.py \ - browser.py \ - c14n.py \ - client.py \ - client_nb.py \ - commands.py \ - debug.py \ - dispatcher.py \ - dispatcher_nb.py \ - features.py \ - features_nb.py \ - filetransfer.py \ - idlequeue.py \ - protocol.py \ - roster.py \ - roster_nb.py \ - session.py \ - simplexml.py \ - transports.py \ +SRCS = __init__.py \ + auth_nb.py \ + bosh.py \ + c14n.py \ + client.py \ + client_nb.py \ + debug.py \ + dispatcher_nb.py \ + features_nb.py \ + idlequeue.py \ + protocol.py \ + proxy_connectors.py \ + roster_nb.py \ + simplexml.py \ + tls_nb.py \ transports_nb.py
include ../../../buildsys.mk
Modified: branches/osx_newbuildsys/src/common/xmpp/__init__.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/__init__.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/__init__.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -26,7 +26,8 @@
"""
-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, auth_nb, transports_nb, roster_nb +import dispatcher_nb, features_nb, idlequeue, bosh, tls_nb, proxy_connectors from client_nb import * from client import * from protocol import *
Deleted: branches/osx_newbuildsys/src/common/xmpp/auth.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/auth.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/auth.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,314 +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') - - def hasher(s): - return sha.new(s).hexdigest() - - def hash_n_times(s, count): - return count and hasher(hash_n_times(s, count-1)) or s - - hash_ = hash_n_times(hasher(hasher(self.password)+token), int(seq)) - 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 'version' not in self._owner.Dispatcher.Stream._document_attrs: 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 Exception: - 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.startswith('"') and value.endswith('"'): value=value[1:-1] - chal[key]=value - if 'qop' in chal and chal['qop']=='auth': - resp={} - resp['username']=self.username - resp['realm']=self._owner.Server - resp['nonce']=chal['nonce'] - resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in - itertools.repeat(random.randint, 7)) - 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 'rspauth' in chal: 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 '' - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/auth_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/auth_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/auth_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -18,14 +18,21 @@ Can be used both for client and transport authentication. ''' from protocol import * -from auth import * from client import PlugIn import sha import base64 import random import itertools import dispatcher_nb +import md5
+import logging +log = logging.getLogger('gajim.c.x.auth_nb') + +def HH(some): return md5.new(some).hexdigest() +def H(some): return md5.new(some).digest() +def C(some): return ':'.join(some) + try: import kerberos have_kerberos = True @@ -137,7 +144,7 @@ ''' 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') + log.error('SASL not supported by server') return self.mecs=[] for mec in feats.getTag('mechanisms', namespace=NS_SASL).getTags('mechanism'): @@ -151,7 +158,7 @@ if 'GSSAPI' in self.mecs and have_kerberos: self.mecs.remove('GSSAPI') self.gss_vc = kerberos.authGSSClientInit('xmpp@' + \ - self._owner.socket._hostfqdn)[1] + self._owner.xmpp_hostname)[1] kerberos.authGSSClientStep(self.gss_vc, '') response = kerberos.authGSSClientResponse(self.gss_vc) node=Node('auth',attrs={'xmlns': NS_SASL, 'mechanism': 'GSSAPI'}, @@ -165,13 +172,13 @@ elif 'PLAIN' in self.mecs: self.mecs.remove('PLAIN') sasl_data='%s\x00%s\x00%s' % (self.username+'@' + self._owner.Server, - self.username, self.password) + self.username, self.password) node=Node('auth', attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'}, - payload=[base64.encodestring(sasl_data).replace('\n','')]) + payload=[base64.encodestring(sasl_data).replace('\n','')]) self.mechanism = 'PLAIN' else: self.startsasl='failure' - self.DEBUG('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.', 'error') + log.error('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.') return self.startsasl='in-process' self._owner.send(node.__str__()) @@ -187,7 +194,7 @@ reason = challenge.getChildren()[0] except Exception: reason = challenge - self.DEBUG('Failed SASL authentification: %s' % reason, 'error') + log.error('Failed SASL authentification: %s' % reason) if len(self.mecs) > 0: # There are other mechanisms to test self.MechanismHandler() @@ -197,19 +204,25 @@ raise NodeProcessed elif challenge.getName() == 'success': self.startsasl='success' - self.DEBUG('Successfully authenticated with remote server.', 'ok') + log.info('Successfully authenticated with remote server.') handlers=self._owner.Dispatcher.dumpHandlers() + + # save old features. They will be used in case we won't get response on + # stream restart after SASL auth (happens with XMPP over BOSH with Openfire) + old_features = self._owner.Dispatcher.Stream.features + self._owner.Dispatcher.PlugOut() - dispatcher_nb.Dispatcher().PlugIn(self._owner) + dispatcher_nb.Dispatcher().PlugIn(self._owner, after_SASL=True, + old_features=old_features) self._owner.Dispatcher.restoreHandlers(handlers) self._owner.User = self.username if self.on_sasl : - self.on_sasl () + self.on_sasl() raise NodeProcessed ########################################3333 incoming_data = challenge.getData() data=base64.decodestring(incoming_data) - self.DEBUG('Got challenge:'+data,'ok') + log.info('Got challenge:' + data) if self.mechanism == 'GSSAPI': if self.gss_step == GSS_STATE_STEP: rc = kerberos.authGSSClientStep(self.gss_vc, incoming_data) @@ -265,7 +278,7 @@ self._owner.send(Node('response', attrs={'xmlns':NS_SASL}).__str__()) else: self.startsasl='failure' - self.DEBUG('Failed SASL authentification: unknown challenge', 'error') + log.error('Failed SASL authentification: unknown challenge') if self.on_sasl : self.on_sasl () raise NodeProcessed @@ -277,7 +290,6 @@ def __init__(self, user, password, resource, on_auth): ''' Caches username, password and resource for auth. ''' PlugIn.__init__(self) - self.DBG_LINE ='gen_auth' self.user = user self.password= password self.resource = resource @@ -286,9 +298,7 @@ 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') + log.info('Querying server about possible auth methods') self.owner = owner
owner.Dispatcher.SendAndWaitForResponse( @@ -297,7 +307,7 @@
def _on_username(self, resp): if not isResultNode(resp): - self.DEBUG('No result node arrived! Aborting...','error') + log.error('No result node arrived! Aborting...') return self.on_auth(None) iq=Iq(typ='set',node=resp) query=iq.getTag('query') @@ -305,7 +315,7 @@ query.setTagData('resource',self.resource)
if query.getTag('digest'): - self.DEBUG("Performing digest authentication",'ok') + log.info("Performing digest authentication") query.setTagData('digest', sha.new(self.owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest()) if query.getTag('password'): @@ -314,7 +324,7 @@ elif query.getTag('token'): token=query.getTagData('token') seq=query.getTagData('sequence') - self.DEBUG("Performing zero-k authentication",'ok') + log.info("Performing zero-k authentication")
def hasher(s): return sha.new(s).hexdigest() @@ -326,51 +336,28 @@ query.setTagData('hash', hash_) self._method='0k' else: - self.DEBUG("Sequre methods unsupported, performing plain text authentication",'warn') + log.warn("Sequre methods unsupported, performing plain text authentication") query.setTagData('password',self.password) self._method='plain' resp=self.owner.Dispatcher.SendAndWaitForResponse(iq, func=self._on_auth)
def _on_auth(self, resp): if isResultNode(resp): - self.DEBUG('Sucessfully authenticated with remove host.','ok') + log.info('Sucessfully authenticated with remove host.') self.owner.User=self.user self.owner.Resource=self.resource self.owner._registered_name=self.owner.User+'@'+self.owner.Server+'/'+self.owner.Resource return self.on_auth(self._method) - self.DEBUG('Authentication failed!','error') + log.error('Authentication failed!') return self.on_auth(None)
- 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) - self._owner.onreceive(self._on_auth_component)
- def _on_auth_component(self, data): - ''' called when we receive some response, after we send the handshake ''' - if data: - self.Dispatcher.ProcessNonBlocking(data) - if not self.handshake: - self.DEBUG('waiting on handshake', 'notify') - return - self._owner.onreceive(None) - self._owner._registered_name=self.user - if self.handshake+1: - return self.on_auth('ok') - self.on_auth(None) - - 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 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.bound=None + def plugin(self, owner): ''' Start resource binding, if allowed at this time. Used internally. ''' if self._owner.Dispatcher.Stream.features: @@ -378,8 +365,23 @@ self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) except NodeProcessed: pass - else: self._owner.RegisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS) + else: + self._owner.RegisterHandler('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): + log.error('Server does not requested binding.') + # we try to bind resource anyway + #self.bound='failure' + self.bound=[] + return + if feats.getTag('session',namespace=NS_SESSION): + self.session=1 + else: + self.session=-1 + self.bound=[] + def plugout(self): ''' Remove Bind handler from owner's dispatcher. Used internally. ''' self._owner.UnregisterHandler('features', self.FeaturesHandler, xmlns=NS_STREAMS) @@ -401,86 +403,37 @@ def _on_bound(self, resp): if isResultNode(resp): self.bound.append(resp.getTag('bind').getTagData('jid')) - self.DEBUG('Successfully bound %s.'%self.bound[-1],'ok') + log.info('Successfully bound %s.'%self.bound[-1]) jid=JID(resp.getTag('bind').getTagData('jid')) self._owner.User=jid.getNode() self._owner.Resource=jid.getResource() self._owner.SendAndWaitForResponse(Protocol('iq', typ='set', payload=[Node('session', attrs={'xmlns':NS_SESSION})]), func=self._on_session) elif resp: - self.DEBUG('Binding failed: %s.' % resp.getTag('error'),'error') + log.error('Binding failed: %s.' % resp.getTag('error')) self.on_bound(None) else: - self.DEBUG('Binding failed: timeout expired.', 'error') + log.error('Binding failed: timeout expired.') self.on_bound(None)
def _on_session(self, resp): self._owner.onreceive(None) if isResultNode(resp): - self.DEBUG('Successfully opened session.', 'ok') + log.info('Successfully opened session.') self.session = 1 self.on_bound('ok') else: - self.DEBUG('Session open failed.', 'error') + log.error('Session open failed.') self.session = 0 self.on_bound(None) self._owner.onreceive(None) if isResultNode(resp): - self.DEBUG('Successfully opened session.', 'ok') + log.info('Successfully opened session.') self.session = 1 self.on_bound('ok') else: - self.DEBUG('Session open failed.', 'error') + log.error('Session open failed.') self.session = 0 self.on_bound(None)
-class NBComponentBind(ComponentBind): - ''' ComponentBind some JID to the current connection to allow - router know of our location. - ''' - 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 Bind(self, domain = None, on_bind = None): - ''' Perform binding. Use provided domain name (if not provided). ''' - def wrapper(resp): - self._on_bound(resp, domain) - self._owner.onreceive(wrapper) - self.on_bind = on_bind - - def _on_bound(self, resp, domain=None): - if resp: - self.Dispatcher.ProcessNonBlocking(resp) - if self.bound is None: - return - self._owner.onreceive(None) - self._owner.SendAndWaitForResponse( - Protocol('bind', attrs={'name':domain}, xmlns=NS_COMPONENT_1), - func=self._on_bind_reponse) - - def _on_bind_reponse(self, resp): - if resp and resp.getAttr('error'): - self.DEBUG('Binding failed: %s.' % resp.getAttr('error'), 'error') - elif resp: - self.DEBUG('Successfully bound.', 'ok') - if self.on_bind: - self.on_bind('ok') - else: - self.DEBUG('Binding failed: timeout expired.', 'error') - if self.on_bind: - self.on_bind(None) - # vim: se ts=3:
Copied: branches/osx_newbuildsys/src/common/xmpp/bosh.py (from rev 10894, trunk/src/common/xmpp/bosh.py) =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/bosh.py (rev 0) +++ branches/osx_newbuildsys/src/common/xmpp/bosh.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,564 @@ +## bosh.py +## +## +## Copyright (C) 2008 Tomas Karasek tom.to.the.k@gmail.com +## +## 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 locale, random, sha +from transports_nb import NonBlockingTransport, NonBlockingHTTPBOSH,\ + CONNECTED, CONNECTING, DISCONNECTED, DISCONNECTING,\ + urisplit, DISCONNECT_TIMEOUT_SECONDS +from protocol import BOSHBody +from simplexml import Node + +import logging +log = logging.getLogger('gajim.c.x.bosh') + +KEY_COUNT = 10 + +# Fake file descriptor - it's used for setting read_timeout in idlequeue for +# BOSH Transport. In TCP-derived transports this is file descriptor of socket. +FAKE_DESCRIPTOR = -1337 + + +class NonBlockingBOSH(NonBlockingTransport): + def __init__(self, raise_event, on_disconnect, idlequeue, estabilish_tls, certs, + xmpp_server, domain, bosh_dict, proxy_creds): + NonBlockingTransport.__init__(self, raise_event, on_disconnect, idlequeue, + estabilish_tls, certs) + + self.bosh_sid = None + if locale.getdefaultlocale()[0]: + self.bosh_xml_lang = locale.getdefaultlocale()[0].split('_')[0] + else: + self.bosh_xml_lang = 'en' + + self.http_version = 'HTTP/1.1' + self.http_persistent = True + self.http_pipelining = bosh_dict['bosh_http_pipelining'] + self.bosh_to = domain + + self.route_host, self.route_port = xmpp_server + + self.bosh_wait = bosh_dict['bosh_wait'] + if not self.http_pipelining: + self.bosh_hold = 1 + else: + self.bosh_hold = bosh_dict['bosh_hold'] + self.bosh_requests = self.bosh_hold + self.bosh_uri = bosh_dict['bosh_uri'] + self.bosh_port = bosh_dict['bosh_port'] + self.bosh_content = bosh_dict['bosh_content'] + self.over_proxy = bosh_dict['bosh_useproxy'] + if estabilish_tls: + self.bosh_secure = 'true' + else: + self.bosh_secure = 'false' + self.use_proxy_auth = bosh_dict['useauth'] + self.proxy_creds = proxy_creds + self.wait_cb_time = None + self.http_socks = [] + self.stanza_buffer = [] + self.prio_bosh_stanzas = [] + self.current_recv_handler = None + self.current_recv_socket = None + self.key_stack = None + self.ack_checker = None + self.after_init = False + self.proxy_dict = {} + if self.over_proxy and self.estabilish_tls: + self.proxy_dict['type'] = 'http' + # with SSL over proxy, we do HTTP CONNECT to proxy to open a channel to + # BOSH Connection Manager + self.proxy_dict['xmpp_server'] = (urisplit(self.bosh_uri)[1], self.bosh_port) + self.proxy_dict['credentials'] = self.proxy_creds + + + def connect(self, conn_5tuple, on_connect, on_connect_failure): + NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure) + + global FAKE_DESCRIPTOR + FAKE_DESCRIPTOR = FAKE_DESCRIPTOR - 1 + self.fd = FAKE_DESCRIPTOR + + self.stanza_buffer = [] + self.prio_bosh_stanzas = [] + + self.key_stack = KeyStack(KEY_COUNT) + self.ack_checker = AckChecker() + self.after_init = True + + self.http_socks.append(self.get_new_http_socket()) + self.tcp_connecting_started() + + self.http_socks[0].connect( + conn_5tuple = conn_5tuple, + on_connect = self._on_connect, + on_connect_failure = self._on_connect_failure) + + def _on_connect(self): + self.peerhost = self.http_socks[0].peerhost + self.ssl_lib = self.http_socks[0].ssl_lib + NonBlockingTransport._on_connect(self) + + + + def set_timeout(self, timeout): + if self.get_state() != DISCONNECTED and self.fd != -1: + NonBlockingTransport.set_timeout(self, timeout) + else: + log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.get_state(), self.fd)) + + def on_http_request_possible(self): + ''' + Called when HTTP request it's possible to send a HTTP request. It can be when + socket is connected or when HTTP response arrived. + There should be always one pending request to BOSH CM. + ''' + log.debug('on_http_req possible, state:\n%s' % self.get_current_state()) + if self.get_state()==DISCONNECTED: return + + #Hack for making the non-secure warning dialog work + if self._owner.got_features: + if (hasattr(self._owner, 'NonBlockingNonSASL') or hasattr(self._owner, 'SASL')): + self.send_BOSH(None) + else: + # If we already got features and no auth module was plugged yet, we are + # probably waiting for confirmation of the "not-secure-connection" dialog. + # We don't send HTTP request in that case. + # see http://lists.jabber.ru/pipermail/ejabberd/2008-August/004027.html + return + else: + self.send_BOSH(None) + + + + def get_socket_in(self, state): + ''' gets sockets in desired state ''' + for s in self.http_socks: + if s.get_state()==state: return s + return None + + + def get_free_socket(self): + ''' Selects and returns socket eligible for sending a data to.''' + if self.http_pipelining: + return self.get_socket_in(CONNECTED) + else: + last_recv_time, tmpsock = 0, None + for s in self.http_socks: + # we're interested only in CONNECTED socket with no requests pending + if s.get_state()==CONNECTED and s.pending_requests==0: + # if there's more of them, we want the one with the least recent data receive + # (lowest last_recv_time) + if (last_recv_time==0) or (s.last_recv_time < last_recv_time): + last_recv_time = s.last_recv_time + tmpsock = s + if tmpsock: + return tmpsock + else: + return None + + + def send_BOSH(self, payload): + ''' + Tries to send a stanza in payload by appeding it to a buffer and plugging a + free socket for writing. + ''' + total_pending_reqs = sum([s.pending_requests for s in self.http_socks]) + + # when called after HTTP response (Payload=None) and when there are already + # some pending requests and no data to send, or when the socket is + # disconnected, we do nothing + if payload is None and \ + total_pending_reqs > 0 and \ + self.stanza_buffer == [] and \ + self.prio_bosh_stanzas == [] or \ + self.get_state()==DISCONNECTED: + return + + # now the payload is put to buffer and will be sent at some point + self.append_stanza(payload) + + # if we're about to make more requests than allowed, we don't send - stanzas will be + # sent after HTTP response from CM, exception is when we're disconnecting - then we + # send anyway + if total_pending_reqs >= self.bosh_requests and self.get_state()!=DISCONNECTING: + log.warn('attemp to make more requests than allowed by Connection Manager:\n%s' % + self.get_current_state()) + return + + # when there's free CONNECTED socket, we plug it for write and the data will + # be sent when write is possible + if self.get_free_socket(): + self.plug_socket() + return + + # if there is a connecting socket, we just wait for when it connects, + # payload will be sent in a sec when the socket connects + if self.get_socket_in(CONNECTING): return + + # being here means there are either DISCONNECTED sockets or all sockets are + # CONNECTED with too many pending requests + s = self.get_socket_in(DISCONNECTED) + + # if we have DISCONNECTED socket, lets connect it and plug for send + if s: + self.connect_and_flush(s) + else: + # otherwise create and connect a new one + ss = self.get_new_http_socket() + self.http_socks.append(ss) + self.connect_and_flush(ss) + return + + def plug_socket(self): + stanza = None + s = self.get_free_socket() + if s: + s._plug_idle(writable=True, readable=True) + else: + log.error('=====!!!!!!!!====> Couldn't get free socket in plug_socket())') + + def build_stanza(self, socket): + ''' + Builds a BOSH body tag from data in buffers and adds key, rid and ack + attributes to it. + This method is called from _do_send() of underlying transport. This is to + ensure rid and keys will be processed in correct order. If I generate them + before plugging a socket for write (and did it for two sockets/HTTP + connections) in parallel, they might be sent in wrong order, which results + in violating the BOSH session and server-side disconnect. + ''' + if self.prio_bosh_stanzas: + stanza, add_payload = self.prio_bosh_stanzas.pop(0) + if add_payload: + stanza.setPayload(self.stanza_buffer) + self.stanza_buffer = [] + else: + stanza = self.boshify_stanzas(self.stanza_buffer) + self.stanza_buffer = [] + + stanza = self.ack_checker.backup_stanza(stanza, socket) + + key, newkey = self.key_stack.get() + if key: + stanza.setAttr('key', key) + if newkey: + stanza.setAttr('newkey', newkey) + + + log.info('sending msg with rid=%s to sock %s' % (stanza.getAttr('rid'), id(socket))) + self.renew_bosh_wait_timeout(self.bosh_wait + 3) + return stanza + + + def on_bosh_wait_timeout(self): + log.error('Connection Manager didn't respond within %s + 3 seconds --> forcing disconnect' % self.bosh_wait) + self.disconnect() + + + def renew_bosh_wait_timeout(self, timeout): + if self.wait_cb_time is not None: + self.remove_bosh_wait_timeout() + sched_time = self.idlequeue.set_alarm(self.on_bosh_wait_timeout, timeout) + self.wait_cb_time = sched_time + + def remove_bosh_wait_timeout(self): + self.idlequeue.remove_alarm( + self.on_bosh_wait_timeout, + self.wait_cb_time) + + def on_persistent_fallback(self, socket): + ''' + Called from underlying transport when server closes TCP connection. + :param socket: disconnected transport object + ''' + if socket.http_persistent: + log.warn('Fallback to nonpersistent HTTP (no pipelining as well)') + socket.http_persistent = False + self.http_persistent = False + self.http_pipelining = False + socket.disconnect(do_callback=False) + self.connect_and_flush(socket) + else: + socket.disconnect() + + + + def handle_body_attrs(self, stanza_attrs): + ''' + Called for each incoming body stanza from dispatcher. Checks body attributes. + ''' + self.remove_bosh_wait_timeout() + + if self.after_init: + if stanza_attrs.has_key('sid'): + # session ID should be only in init response + self.bosh_sid = stanza_attrs['sid'] + + if stanza_attrs.has_key('requests'): + self.bosh_requests = int(stanza_attrs['requests']) + + if stanza_attrs.has_key('wait'): + self.bosh_wait = int(stanza_attrs['wait']) + self.after_init = False + + ack = None + if stanza_attrs.has_key('ack'): + ack = stanza_attrs['ack'] + self.ack_checker.process_incoming_ack(ack=ack, + socket=self.current_recv_socket) + + if stanza_attrs.has_key('type'): + if stanza_attrs['type'] in ['terminate', 'terminal']: + condition = 'n/a' + if stanza_attrs.has_key('condition'): + condition = stanza_attrs['condition'] + if condition == 'n/a': + log.info('Received sesion-ending terminating stanza') + else: + log.error('Received terminating stanza: %s - %s' % (condition, + bosh_errors[condition])) + self.disconnect() + return + + if stanza_attrs['type'] == 'error': + # recoverable error + pass + return + + + def append_stanza(self, stanza): + ''' appends stanza to a buffer to send ''' + if stanza: + if isinstance(stanza, tuple): + # stanza is tuple of BOSH stanza and bool value for whether to add payload + self.prio_bosh_stanzas.append(stanza) + else: + # stanza is XMPP stanza. Will be boshified before send. + self.stanza_buffer.append(stanza) + + + def send(self, stanza, now=False): + self.send_BOSH(stanza) + + + + def get_current_state(self): + t = '------ SOCKET_ID\tSOCKET_STATE\tPENDING_REQS\n' + for s in self.http_socks: + t = '%s------ %s\t%s\t%s\n' % (t,id(s), s.get_state(), s.pending_requests) + t = '%s------ prio stanzas: %s, queued XMPP stanzas: %s, not_acked stanzas: %s' \ + % (t, self.prio_bosh_stanzas, self.stanza_buffer, + self.ack_checker.get_not_acked_rids()) + return t + + + def connect_and_flush(self, socket): + socket.connect( + conn_5tuple = self.conn_5tuple, + on_connect = self.on_http_request_possible, + on_connect_failure = self.disconnect) + + + def boshify_stanzas(self, stanzas=[], body_attrs=None): + ''' wraps zero to many stanzas by body tag with xmlns and sid ''' + log.debug('boshify_staza - type is: %s, stanza is %s' % (type(stanzas), stanzas)) + tag = BOSHBody(attrs={'sid': self.bosh_sid}) + tag.setPayload(stanzas) + return tag + + + def send_init(self, after_SASL=False): + if after_SASL: + t = BOSHBody( + attrs={ 'to': self.bosh_to, + 'sid': self.bosh_sid, + 'xml:lang': self.bosh_xml_lang, + 'xmpp:restart': 'true', + 'secure': self.bosh_secure, + 'xmlns:xmpp': 'urn:xmpp:xbosh'}) + else: + t = BOSHBody( + attrs={ 'content': self.bosh_content, + 'hold': str(self.bosh_hold), + 'route': '%s:%s' % (self.route_host, self.route_port), + 'to': self.bosh_to, + 'wait': str(self.bosh_wait), + 'xml:lang': self.bosh_xml_lang, + 'xmpp:version': '1.0', + 'ver': '1.6', + 'xmlns:xmpp': 'urn:xmpp:xbosh'}) + self.send_BOSH((t,True)) + + def start_disconnect(self): + NonBlockingTransport.start_disconnect(self) + self.renew_bosh_wait_timeout(DISCONNECT_TIMEOUT_SECONDS) + self.send_BOSH( + (BOSHBody(attrs={'sid': self.bosh_sid, 'type': 'terminate'}), True)) + + + def get_new_http_socket(self): + http_dict = {'http_uri': self.bosh_uri, + 'http_port': self.bosh_port, + 'http_version': self.http_version, + 'http_persistent': self.http_persistent, + 'add_proxy_headers': self.over_proxy and not self.estabilish_tls} + if self.use_proxy_auth: + http_dict['proxy_user'], http_dict['proxy_pass'] = self.proxy_creds + + s = NonBlockingHTTPBOSH( + raise_event=self.raise_event, + on_disconnect=self.disconnect, + idlequeue = self.idlequeue, + estabilish_tls = self.estabilish_tls, + certs = self.certs, + on_http_request_possible = self.on_http_request_possible, + http_dict = http_dict, + proxy_dict = self.proxy_dict, + on_persistent_fallback = self.on_persistent_fallback) + + s.onreceive(self.on_received_http) + s.set_stanza_build_cb(self.build_stanza) + return s + + + def onreceive(self, recv_handler): + if recv_handler is None: + recv_handler = self._owner.Dispatcher.ProcessNonBlocking + self.current_recv_handler = recv_handler + + + def on_received_http(self, data, socket): + self.current_recv_socket = socket + self.current_recv_handler(data) + + + def disconnect(self, do_callback=True): + self.remove_bosh_wait_timeout() + if self.get_state() == DISCONNECTED: return + self.fd = -1 + for s in self.http_socks: + s.disconnect(do_callback=False) + NonBlockingTransport.disconnect(self, do_callback) + + +def get_rand_number(): + # 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) + # it's also used for sequence key initialization + r = random.Random() + r.seed() + return r.getrandbits(50) + + + +class AckChecker(): + ''' + Class for generating rids and generating and checking acknowledgements in + BOSH messages. + ''' + def __init__(self): + self.rid = get_rand_number() + self.ack = 1 + self.last_rids = {} + self.not_acked = [] + + + def get_not_acked_rids(self): return [rid for rid, st in self.not_acked] + + def backup_stanza(self, stanza, socket): + socket.pending_requests += 1 + rid = self.get_rid() + self.not_acked.append((rid, stanza)) + stanza.setAttr('rid', str(rid)) + self.last_rids[socket]=rid + + if self.rid != self.ack + 1: + stanza.setAttr('ack', str(self.ack)) + return stanza + + def process_incoming_ack(self, socket, ack=None): + socket.pending_requests -= 1 + if ack: + ack = int(ack) + else: + ack = self.last_rids[socket] + + i = len([rid for rid, st in self.not_acked if ack >= rid]) + self.not_acked = self.not_acked[i:] + + self.ack = ack + + + def get_rid(self): + self.rid = self.rid + 1 + return self.rid + + + + + +class KeyStack(): + ''' + Class implementing key sequences for BOSH messages + ''' + def __init__(self, count): + self.count = count + self.keys = [] + self.reset() + self.first_call = True + + def reset(self): + seed = str(get_rand_number()) + self.keys = [sha.new(seed).hexdigest()] + for i in range(self.count-1): + curr_seed = self.keys[i] + self.keys.append(sha.new(curr_seed).hexdigest()) + + def get(self): + if self.first_call: + self.first_call = False + return (None, self.keys.pop()) + + if len(self.keys)>1: + return (self.keys.pop(), None) + else: + last_key = self.keys.pop() + self.reset() + new_key = self.keys.pop() + return (last_key, new_key) + +# http://www.xmpp.org/extensions/xep-0124.html#errorstatus-terminal +bosh_errors = { + 'n/a': 'none or unknown condition in terminating body stanza', + 'bad-request': 'The format of an HTTP header or binding element received from the client is unacceptable (e.g., syntax error), or Script Syntax is not supported.', + 'host-gone': 'The target domain specified in the "to" attribute or the target host or port specified in the "route" attribute is no longer serviced by the connection manager.', + 'host-unknown': 'The target domain specified in the "to" attribute or the target host or port specified in the "route" attribute is unknown to the connection manager.', + 'improper-addressing': 'The initialization element lacks a "to" or "route" attribute (or the attribute has no value) but the connection manager requires one.', + 'internal-server-error': 'The connection manager has experienced an internal error that prevents it from servicing the request.', + 'item-not-found': '(1) "sid" is not valid, (2) "stream" is not valid, (3) "rid" is larger than the upper limit of the expected window, (4) connection manager is unable to resend response, (5) "key" sequence is invalid', + 'other-request': 'Another request being processed at the same time as this request caused the session to terminate.', + 'policy-violation': 'The client has broken the session rules (polling too frequently, requesting too frequently, too many simultaneous requests).', + 'remote-connection-failed': 'The connection manager was unable to connect to, or unable to connect securely to, or has lost its connection to, the server.', + 'remote-stream-error': 'Encapsulates an error in the protocol being transported.', + 'see-other-uri': 'The connection manager does not operate at this URI (e.g., the connection manager accepts only SSL or TLS connections at some https: URI rather than the http: URI requested by the client). The client may try POSTing to the URI in the content of the <uri/> child element.', + 'system-shutdown': 'The connection manager is being shut down. All active HTTP sessions are being terminated. No new sessions can be created.', + 'undefined-condition': 'The error is not one of those defined herein; the connection manager SHOULD include application-specific information in the content of the <body/> wrapper.' +}
Deleted: branches/osx_newbuildsys/src/common/xmpp/browser.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/browser.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/browser.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,221 +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 - -DBG_BROWSER = "Browser" - -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) - self.DBG_LINE = DBG_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) - owner.debug_flags.append(DBG_BROWSER) - - 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 jid in self._handlers: 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 i in cur: cur=cur[i] - elif set_ and i!='': cur[i]={dict:cur,str:i}; cur=cur[i] - elif set_ or '' in cur: return cur,'' - else: return None,None - if 1 in cur or set_: return cur,1 - raise Exception("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 isinstance(handler, dict): lst=handler['items'] - else: lst=handler(conn,request,'items') - if lst is 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 isinstance(handler, dict): dt=handler['info'] - else: dt=handler(conn,request,'info') - if dt is 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 'xdata' in dt: q.addChild(node=dt['xdata']) - conn.send(rep) - raise NodeProcessed - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/client.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/client.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/client.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -14,311 +14,55 @@
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
-""" +''' Provides PlugIn class functionality to develop extentions for xmpppy. Also provides Client and Component classes implementations as the examples of xmpppy structures usage. These classes can be used for simple applications "AS IS" though. -""" +'''
-import socket -import debug -Debug=debug -Debug.DEBUGGING_IS_ON=1 -Debug.Debug.colors['socket']=debug.color_dark_gray -Debug.Debug.colors['CONNECTproxy']=debug.color_dark_gray -Debug.Debug.colors['nodebuilder']=debug.color_brown -Debug.Debug.colors['client']=debug.color_cyan -Debug.Debug.colors['component']=debug.color_cyan -Debug.Debug.colors['dispatcher']=debug.color_green -Debug.Debug.colors['browser']=debug.color_blue -Debug.Debug.colors['auth']=debug.color_yellow -Debug.Debug.colors['roster']=debug.color_magenta -Debug.Debug.colors['ibb']=debug.color_yellow +import logging +log = logging.getLogger('gajim.c.x.plugin')
-Debug.Debug.colors['down']=debug.color_brown -Debug.Debug.colors['up']=debug.color_brown -Debug.Debug.colors['data']=debug.color_brown -Debug.Debug.colors['ok']=debug.color_green -Debug.Debug.colors['warn']=debug.color_yellow -Debug.Debug.colors['error']=debug.color_red -Debug.Debug.colors['start']=debug.color_dark_gray -Debug.Debug.colors['stop']=debug.color_dark_gray -Debug.Debug.colors['sent']=debug.color_yellow -Debug.Debug.colors['got']=debug.color_bright_cyan - -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() + ''' Common xmpppy plugins infrastructure: plugging in/out, debugging. ''' + def __init__(self): + self._exported_methods=[]
- 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 self.__class__.__name__ in owner.__dict__: - return self.DEBUG('Plugging ignored: another instance already plugged.','error') - self._old_owners_methods=[] - for method in self._exported_methods: - if method.__name__ in owner.__dict__: - self._old_owners_methods.append(owner.__dict__[method.__name__]) - owner.__dict__[method.__name__]=method - owner.__dict__[self.__class__.__name__]=self - if 'plugin' in self.__class__.__dict__: return self.plugin(owner) + def PlugIn(self,owner): + ''' Attach to main instance and register ourself and all our staff in it. ''' + self._owner=owner + log.info('Plugging %s __INTO__ %s' % (self,self._owner)) + if self.__class__.__name__ in owner.__dict__: + log.debug('Plugging ignored: another instance already plugged.') + return + self._old_owners_methods=[] + for method in self._exported_methods: + if method.__name__ in owner.__dict__: + self._old_owners_methods.append(owner.__dict__[method.__name__]) + owner.__dict__[method.__name__]=method + if self.__class__.__name__.endswith('Dispatcher'): + # FIXME: I need BOSHDispatcher or XMPPDispatcher on .Dispatcher + # there must be a better way.. + owner.__dict__['Dispatcher']=self + else: + owner.__dict__[self.__class__.__name__]=self
- 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 'plugout' in self.__class__.__dict__: return self.plugout() - del self._owner + # following commented line will not work for classes inheriting plugin() + #if self.__class__.__dict__.has_key('plugin'): return self.plugin(owner) + if hasattr(self,'plugin'): return self.plugin(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 PlugOut(self): + ''' Unregister all our staff from main instance and detach from it. ''' + log.info('Plugging %s __OUT__ of %s.' % (self, self._owner)) + 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 + if self.__class__.__name__.endswith('Dispatcher'): + del self._owner.__dict__['Dispatcher'] + else: + del self._owner.__dict__[self.__class__.__name__] + #if self.__class__.__dict__.has_key('plugout'): return self.plugout() + if hasattr(self,'plugout'): return self.plugout() + del self._owner
-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 not isinstance(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 'TLS' in self.__dict__: 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 'NonSASL' in self.__dict__: self.NonSASL.PlugOut() - if 'SASL' in self.__dict__: self.SASL.PlugOut() - if 'TLS' in self.__dict__: self.TLS.PlugOut() - if 'HTTPPROXYsocket' in self.__dict__: self.HTTPPROXYsocket.PlugOut() - if 'TCPsocket' in self.__dict__: 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 'version' in self.Dispatcher.Stream._document_attrs 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 'version' not in self.Dispatcher.Stream._document_attrs 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 'version' in self.Dispatcher.Stream._document_attrs 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 'Roster' not in self.__dict__: 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 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 Exception: - self.DEBUG(self.DBG,"Failed to authenticate %s"%name,'error') - # vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/client_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/client_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/client_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -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 @@ -16,64 +16,67 @@
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
-''' -Provides PlugIn class functionality to develop extentions for xmpppy. -Also provides Client and Component classes implementations as the -examples of xmpppy structures usage. -These classes can be used for simple applications "AS IS" though. -''' +import socket
-import transports_nb, dispatcher_nb, auth_nb, roster_nb +import transports_nb, dispatcher_nb, auth_nb, roster_nb, protocol, bosh from client import *
-class NBCommonClient(CommonClient): - ''' 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): - ''' 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'] . ''' +from protocol import NS_TLS
- if isinstance(self, NonBlockingClient): - self.Namespace, self.DBG = 'jabber:client', DBG_CLIENT - elif isinstance(self, NBCommonClient): - self.Namespace, self.DBG = dispatcher_nb.NS_COMPONENT_ACCEPT, DBG_COMPONENT +import logging +log = logging.getLogger('gajim.c.x.client_nb')
+ +class NonBlockingClient: + ''' + Client class is XMPP connection mountpoint. Objects for authentication, + network communication, roster, xml parsing ... are plugged to client object. + Client implements the abstract behavior - mostly negotioation and callbacks + handling, whereas underlying modules take care of feature-specific logic. + ''' + def __init__(self, domain, idlequeue, caller=None): + ''' + Caches connection data: + :param domain: domain - for to: attribute (from account info) + :param idlequeue: processing idlequeue + :param caller: calling object - it has to implement methods _event_dispatcher + which is called from dispatcher instance + ''' + self.Namespace = protocol.NS_CLIENT self.defaultNamespace = self.Namespace + + self.idlequeue = idlequeue self.disconnect_handlers = [] - self.Server = server - self.Port = port
- # Who initiated this client - # Used to register the EventDispatcher + self.Server = domain + self.xmpp_hostname = None # FQDN hostname to connect to + + # caller is who initiated this client, it is ineeded to register the EventDispatcher self._caller = caller - if debug and not isinstance(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.connected = '' - self._component=0 - self.idlequeue = None self.socket = None - self.on_connect = on_connect - self.on_proxy_failure = on_proxy_failure - self.on_connect_failure = on_connect_failure + self.on_connect = None + self.on_proxy_failure = None + self.on_connect_failure = None + self.proxy = None + self.got_features = False + self.stream_started = False + self.disconnecting = False + self.protocol_type = 'XMPP'
- def set_idlequeue(self, idlequeue): - self.idlequeue = idlequeue + def disconnect(self, message=''): + ''' + Called on disconnection - disconnect callback is picked based on state of the + client. + '''
- def disconnected(self): - ''' Called on disconnection. Calls disconnect handlers and cleans things up. ''' - self.connected='' - self.DEBUG(self.DBG,'Disconnect detected','stop') - for i in reversed(self.disconnect_handlers): - i() + # to avoid recursive calls + if self.disconnecting: return + + log.info('Disconnecting NBClient: %s' % message) + if 'NonBlockingRoster' in self.__dict__: self.NonBlockingRoster.PlugOut() if 'NonBlockingBind' in self.__dict__: @@ -82,172 +85,319 @@ self.NonBlockingNonSASL.PlugOut() if 'SASL' in self.__dict__: self.SASL.PlugOut() - if 'NonBlockingTLS' in self.__dict__: - self.NonBlockingTLS.PlugOut() - if 'NBHTTPPROXYsocket' in self.__dict__: - self.NBHTTPPROXYsocket.PlugOut() - if 'NBSOCKS5PROXYsocket' in self.__dict__: - self.NBSOCKS5PROXYsocket.PlugOut() - if 'NonBlockingTcp' in self.__dict__: - self.NonBlockingTcp.PlugOut() + if 'NonBlockingTCP' in self.__dict__: + self.NonBlockingTCP.PlugOut() + if 'NonBlockingHTTP' in self.__dict__: + self.NonBlockingHTTP.PlugOut() + if 'NonBlockingBOSH' in self.__dict__: + self.NonBlockingBOSH.PlugOut()
- def reconnectAndReauth(self): - ''' Just disconnect. We do reconnecting in connection.py ''' - self.disconnect() - return '' + connected = self.connected + stream_started = self.stream_started
- def connect(self,server=None,proxy=None, ssl=None, on_stream_start = None): - ''' Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream. ''' - if not server: - server = (self.Server, self.Port) - self._Server, self._Proxy, self._Ssl = server , proxy, ssl - self.on_stream_start = on_stream_start + self.connected = '' + self.stream_started = False + + self.disconnecting = True + + log.debug('Client disconnected..') + if connected == '': + # if we're disconnecting before connection to XMPP sever is opened, we don't + # call disconnect handlers but on_connect_failure callback + if self.proxy: + # with proxy, we have different failure callback + log.debug('calling on_proxy_failure cb') + self.on_proxy_failure(reason=message) + else: + log.debug('ccalling on_connect_failure cb') + self.on_connect_failure() + else: + # we are connected to XMPP server + if not stream_started: + # if error occur before XML stream was opened, e.g. no response on init + # request, we call the on_connect_failure callback because proper + # connection is not estabilished yet and it's not a proxy issue + log.debug('calling on_connect_failure cb') + self.on_connect_failure() + else: + # with open connection, we are calling the disconnect handlers + for i in reversed(self.disconnect_handlers): + log.debug('Calling disconnect handler %s' % i) + i() + self.disconnecting = False + + + def connect(self, on_connect, on_connect_failure, hostname=None, port=5222, + on_proxy_failure=None, proxy=None, secure_tuple=(None, None, None)): + ''' + Open XMPP connection (open XML streams in both directions). + :param on_connect: called after stream is successfully opened + :param on_connect_failure: called when error occures during connection + :param hostname: hostname of XMPP server from SRV request + :param port: port number of XMPP server + :param on_proxy_failure: called if error occurres during TCP connection to + proxy server or during proxy connecting process + :param proxy: dictionary with proxy data. It should contain at least values + for keys 'host' and 'port' - connection details for proxy server and + optionally keys 'user' and 'pass' as proxy credentials + :param secure_tuple: tuple of (desired connection type, cacerts and mycerts) + connection type can be 'ssl' - TLS estabilished after TCP connection, + 'tls' - TLS estabilished after negotiation with starttls, or 'plain'. + cacerts, mycerts - see tls_nb.NonBlockingTLS constructor for more details + ''' + + self.on_connect = on_connect + self.on_connect_failure=on_connect_failure + self.on_proxy_failure = on_proxy_failure + self.secure, self.cacerts, self.mycerts = secure_tuple + self.Connection = None + self.Port = port + self.proxy = proxy + + if hostname: + self.xmpp_hostname = hostname + else: + self.xmpp_hostname = self.Server + + estabilish_tls = self.secure == 'ssl' + certs = (self.cacerts, self.mycerts) + + proxy_dict = {} + tcp_host = self.xmpp_hostname + tcp_port = self.Port + if proxy: - if 'type' in proxy: - type_ = proxy['type'] - if type_ == 'socks5': - self.socket = transports_nb.NBSOCKS5PROXYsocket( - self._on_connected, self._on_proxy_failure, - self._on_connected_failure, proxy, server) - elif type_ == 'http': - self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected, - self._on_proxy_failure, self._on_connected_failure, proxy, - server) + # with proxies, client connects to proxy instead of directly to + # XMPP server ((hostname, port)) + # tcp_host is hostname of machine used for socket connection + # (DNS request will be done for proxy or BOSH CM hostname) + tcp_host, tcp_port, proxy_user, proxy_pass = \ + transports_nb.get_proxy_data_from_dict(proxy) + + + if proxy['type'] == 'bosh': + self.socket = bosh.NonBlockingBOSH( + on_disconnect = self.disconnect, + raise_event = self.raise_event, + idlequeue = self.idlequeue, + estabilish_tls = estabilish_tls, + certs = certs, + proxy_creds = (proxy_user, proxy_pass), + xmpp_server = (self.xmpp_hostname, self.Port), + domain = self.Server, + bosh_dict = proxy) + self.protocol_type = 'BOSH' + self.wait_for_restart_response = proxy['bosh_wait_for_restart_response'] + else: - self.socket = transports_nb.NBHTTPPROXYsocket(self._on_connected, - self._on_proxy_failure, self._on_connected_failure, proxy, - server) - else: - self.connected = 'tcp' - self.socket = transports_nb.NonBlockingTcp(self._on_connected, - self._on_connected_failure, server) + proxy_dict['type'] = proxy['type'] + proxy_dict['xmpp_server'] = (self.xmpp_hostname, self.Port) + proxy_dict['credentials'] = (proxy_user, proxy_pass) + + if not proxy or proxy['type'] != 'bosh': + self.socket = transports_nb.NonBlockingTCP( + on_disconnect = self.disconnect, + raise_event = self.raise_event, + idlequeue = self.idlequeue, + estabilish_tls = estabilish_tls, + certs = certs, + proxy_dict = proxy_dict) + self.socket.PlugIn(self) - return True
- def get_attrs(self, on_stream_start): - self.on_stream_start = on_stream_start - self.onreceive(self._on_receive_document_attrs) + self._resolve_hostname( + hostname=tcp_host, + port=tcp_port, + on_success=self._try_next_ip)
- def _on_proxy_failure(self, reason): - if self.on_proxy_failure: - self.on_proxy_failure(reason) + def _resolve_hostname(self, hostname, port, on_success): + ''' wrapper for getaddinfo call. FIXME: getaddinfo blocks''' + try: + self.ip_addresses = socket.getaddrinfo(hostname,port, + socket.AF_UNSPEC,socket.SOCK_STREAM) + except socket.gaierror, (errnum, errstr): + self.disconnect(message='Lookup failure for %s:%s, hostname: %s - %s' % + (self.Server, self.Port, hostname, errstr)) + else: + on_success()
- def _on_connected_failure(self, retry = None): - if self.socket: - self.socket.disconnect() - if self.on_connect_failure: - self.on_connect_failure(retry) + def _try_next_ip(self, err_message=None): + '''iterates over IP addresses from getaddrinfo''' + if err_message: + log.debug('While looping over DNS A records: %s' % err_message) + if self.ip_addresses == []: + msg = 'Run out of hosts for name %s:%s.' % (self.Server, self.Port) + msg = msg + ' Error for last IP: %s' % err_message + self.disconnect(msg) + else: + self.current_ip = self.ip_addresses.pop(0) + self.socket.connect( + conn_5tuple=self.current_ip, + on_connect=lambda: self._xmpp_connect(socket_type='plain'), + on_connect_failure=self._try_next_ip)
- def _on_connected(self): - # FIXME: why was this needed? Please note that we're working - # in nonblocking mode, and this handler is actually called - # as soon as connection is initiated, NOT when connection - # succeeds, as the name suggests. - # # connect succeeded, so no need of this callback anymore - # self.on_connect_failure = None - self.connected = 'tcp' - if self._Ssl: - transports_nb.NonBlockingTLS().PlugIn(self, now=1) - if not self.Connection: # ssl error, stream is closed - return - self.connected = 'ssl' - self.onreceive(self._on_receive_document_attrs) - dispatcher_nb.Dispatcher().PlugIn(self)
- def _on_receive_document_attrs(self, data): - if data: - self.Dispatcher.ProcessNonBlocking(data) - if not hasattr(self, 'Dispatcher') or \ - self.Dispatcher.Stream._document_attrs is None: - return - self.onreceive(None) - if 'version' in self.Dispatcher.Stream._document_attrs and \ - self.Dispatcher.Stream._document_attrs['version'] == '1.0': - self.onreceive(self._on_receive_stream_features) - return - if self.on_stream_start: - self.on_stream_start() - self.on_stream_start = None - return True + def incoming_stream_version(self): + ''' gets version of xml stream''' + if self.Dispatcher.Stream._document_attrs.has_key('version'): + return self.Dispatcher.Stream._document_attrs['version'] + else: + return None
- def _on_receive_stream_features(self, data): - if data: - self.Dispatcher.ProcessNonBlocking(data) - if not self.Dispatcher.Stream.features: - return - # pass # If we get version 1.0 stream the features tag MUST BE presented - self.onreceive(None) - if self.on_stream_start: - self.on_stream_start() - self.on_stream_start = None - return True + def _xmpp_connect(self, socket_type): + ''' + Starts XMPP connecting process - opens the XML stream. Is called after TCP + connection is estabilished or after switch to TLS when successfully + negotiated with <starttls>. + ''' + if socket_type == 'plain' and self.Connection.ssl_lib: + socket_type = 'ssl' + self.connected = socket_type + self._xmpp_connect_machine()
-class NonBlockingClient(NBCommonClient): - ''' 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.''' - self.__secure = secure - self.Connection = None - NBCommonClient.connect(self, server = server, proxy = proxy, ssl = secure, - on_stream_start = self._on_tcp_stream_start) - return self.connected
+ def _xmpp_connect_machine(self, mode=None, data=None): + ''' + Finite automaton taking care of stream opening and features tag + handling. Calls _on_stream_start when stream is started, and disconnect() + on failure. + ''' + log.info('-------------xmpp_connect_machine() >> mode: %s, data: %s...' % (mode,str(data)[:20] ))
- def _is_connected(self): - self.onreceive(None) - if self.on_connect: - self.on_connect(self, self.connected) - self.on_connect_failure = None - self.on_connect = None + def on_next_receive(mode): + log.info('setting %s on next receive' % mode) + if mode is None: + self.onreceive(None) + else: + self.onreceive(lambda _data:self._xmpp_connect_machine(mode, _data))
- def _on_tcp_stream_start(self): - if not self.connected or self.__secure is not None and not self.__secure: - self._is_connected() - return True - self.isplugged = True + if not mode: + # starting state + if self.__dict__.has_key('Dispatcher'): + self.Dispatcher.PlugOut() + self.got_features = False + d = dispatcher_nb.Dispatcher().PlugIn(self) + on_next_receive('RECEIVE_DOCUMENT_ATTRIBUTES') + + elif mode == 'FAILURE': + self.disconnect('During XMPP connect: %s' % data) + + elif mode == 'RECEIVE_DOCUMENT_ATTRIBUTES': + if data: + self.Dispatcher.ProcessNonBlocking(data) + if not hasattr(self, 'Dispatcher') or \ + self.Dispatcher.Stream._document_attrs is None: + self._xmpp_connect_machine( + mode='FAILURE', + data='Error on stream open') + if self.incoming_stream_version() == '1.0': + if not self.got_features: + on_next_receive('RECEIVE_STREAM_FEATURES') + else: + log.info('got STREAM FEATURES in first recv') + self._xmpp_connect_machine(mode='STREAM_STARTED') + + else: + log.info('incoming stream version less than 1.0') + self._xmpp_connect_machine(mode='STREAM_STARTED') + + elif mode == 'RECEIVE_STREAM_FEATURES': + if data: + # sometimes <features> are received together with document + # attributes and sometimes on next receive... + self.Dispatcher.ProcessNonBlocking(data) + if not self.got_features: + self._xmpp_connect_machine( + mode='FAILURE', + data='Missing <features> in 1.0 stream') + else: + log.info('got STREAM FEATURES in second recv') + self._xmpp_connect_machine(mode='STREAM_STARTED') + + elif mode == 'STREAM_STARTED': + self._on_stream_start() + + def _on_stream_start(self): + ''' + Called after XMPP stream is opened. + TLS negotiation may follow after esabilishing a stream. + ''' + self.stream_started = True self.onreceive(None) - transports_nb.NonBlockingTLS().PlugIn(self) - if not self.Connection: # ssl error, stream is closed - return True - if 'version' not in self.Dispatcher.Stream._document_attrs or \ - not self.Dispatcher.Stream._document_attrs['version']=='1.0': - self._is_connected() - return - if not self.Dispatcher.Stream.features.getTag('starttls'): - self._is_connected() - return - self.onreceive(self._on_receive_starttls) + if self.connected == 'plain': + if self.secure == 'plain': + # if we want plain connection, we're done now + self._on_connect() + return + if self.incoming_stream_version() != '1.0': + # if stream version is less than 1.0, we can't do more + log.warn('While connecting with type = "tls": stream version is less than 1.0') + self._on_connect() + return + if not self.Dispatcher.Stream.features.getTag('starttls'): + # if server doesn't advertise TLS in init response, we can't do more + log.warn('While connecting with type = "tls": TLS unsupported by remote server') + self._on_connect() + return + # otherwise start TLS negotioation + self.stream_started = False + log.info("TLS supported by remote server. Requesting TLS start.") + self._tls_negotiation_handler() + elif self.connected in ['ssl', 'tls']: + self._on_connect()
- def _on_receive_starttls(self, data): - if data: - self.Dispatcher.ProcessNonBlocking(data) - if not self.NonBlockingTLS.starttls: - return + def _tls_negotiation_handler(self, con=None, tag=None): + ''' takes care of TLS negotioation with <starttls> ''' + log.info('-------------tls_negotiaton_handler() >> tag: %s' % tag) + if not con and not tag: + # starting state when we send the <starttls> + self.RegisterHandlerOnce('proceed', self._tls_negotiation_handler, + xmlns=NS_TLS) + self.RegisterHandlerOnce('failure', self._tls_negotiation_handler, + xmlns=NS_TLS) + self.send('<starttls xmlns="%s"/>' % NS_TLS) + else: + # we got <proceed> or <failure> + if tag.getNamespace() <> NS_TLS: + self.disconnect('Unknown namespace: %s' % tag.getNamespace()) + return + tagname = tag.getName() + if tagname == 'failure': + self.disconnect('TLS <failure> received: %s' % tag) + return + log.info('Got starttls proceed response. Switching to TLS/SSL...') + # following call wouldn't work for BOSH transport but it doesn't matter + # because <starttls> negotiation with BOSH is forbidden + self.Connection.tls_init( + on_succ = lambda: self._xmpp_connect(socket_type='tls'), + on_fail = lambda: self.disconnect('error while etabilishing TLS')) + + def _on_connect(self): + ''' preceeds call of on_connect callback ''' self.onreceive(None) - if not hasattr(self, 'NonBlockingTLS') or self.NonBlockingTLS.starttls != 'success': - self.event('tls_failed') - self._is_connected() - return - self.connected = 'tls' - self.onreceive(None) - self._is_connected() - return True + self.on_connect(self, self.connected)
+ def raise_event(self, event_type, data): + ''' + raises event to connection instance - DATA_SENT and DATA_RECIVED events are + used in XML console to show XMPP traffic + ''' + log.info('raising event from transport: :::::%s::::\n_____________\n%s\n_____________\n' % (event_type,data)) + if hasattr(self, 'Dispatcher'): + self.Dispatcher.Event('', event_type, data) + + # follows code for authentication, resource bind, session and roster download + # def auth(self, user, password, resource = '', sasl = 1, on_auth = None): - ''' Authenticate connnection and bind resource. If resource is not provided - random one or library name used. ''' + ''' + Authenticate connnection and bind resource. If resource is not provided + random one or library name used. + ''' self._User, self._Password, self._Resource, self._sasl = user, password, resource, sasl self.on_auth = on_auth - self.get_attrs(self._on_doc_attrs) + self._on_doc_attrs() return - + def _on_old_auth(self, res): if res: self.connected += '+old_auth' @@ -263,14 +413,13 @@ self._Resource = 'xmpppy' auth_nb.NonBlockingNonSASL(self._User, self._Password, self._Resource, self._on_old_auth).PlugIn(self) return - self.onreceive(self._on_start_sasl) self.SASL.auth() return True - + def _on_start_sasl(self, data=None): if data: self.Dispatcher.ProcessNonBlocking(data) - if 'SASL' not in self.__dict__: + if not self.__dict__.has_key('SASL'): # SASL is pluged out, possible disconnect return if self.SASL.startsasl == 'in-process': @@ -280,21 +429,19 @@ # wrong user/pass, stop auth self.connected = None self._on_sasl_auth(None) - self.SASL.PlugOut() elif self.SASL.startsasl == 'success': auth_nb.NonBlockingBind().PlugIn(self) self.onreceive(self._on_auth_bind) return True - + def _on_auth_bind(self, data): if data: self.Dispatcher.ProcessNonBlocking(data) - if not hasattr(self, 'NonBlockingBind') or self.NonBlockingBind.bound is \ - None: + if self.NonBlockingBind.bound is None: return self.NonBlockingBind.NonBlockingBind(self._Resource, self._on_sasl_auth) return True - + def _on_sasl_auth(self, res): self.onreceive(None) if res: @@ -303,15 +450,16 @@ else: self.on_auth(self, None)
+ def initRoster(self): ''' Plug in the roster. ''' - if 'NonBlockingRoster' not in self.__dict__: + if not self.__dict__.has_key('NonBlockingRoster'): roster_nb.NonBlockingRoster().PlugIn(self)
def getRoster(self, on_ready = None): ''' Return the Roster instance, previously plugging it in and requesting roster from server if needed. ''' - if 'NonBlockingRoster' in self.__dict__: + if self.__dict__.has_key('NonBlockingRoster'): return self.NonBlockingRoster.getRoster(on_ready) return None
@@ -321,89 +469,35 @@ if requestRoster: roster_nb.NonBlockingRoster().PlugIn(self) self.send(dispatcher_nb.Presence(to=jid, typ=typ))
-class Component(NBCommonClient): - ''' 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, on_connect = None, on_connect_failure = None): - ''' 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()".''' - NBCommonClient.__init__(self, server, port=port, debug=debug) - self.typ = typ - self.component=component - if domains: - self.domains=domains - else: - self.domains=[server] - self.on_connect_component = on_connect - self.on_connect_failure = on_connect_failure + # following methods are moved from blocking client class from xmpppy: + def RegisterDisconnectHandler(self,handler): + ''' Register handler that will be called on disconnect.''' + self.disconnect_handlers.append(handler)
- 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] - NBCommonClient.connect(self, server=server, proxy=proxy, - on_connect = self._on_connect, on_connect_failure = self.on_connect_failure) + def UnregisterDisconnectHandler(self,handler): + ''' Unregister handler that is called on disconnect.''' + self.disconnect_handlers.remove(handler)
- def _on_connect(self): - if 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_nb.Message) - self.Dispatcher.RegisterProtocol('presence',dispatcher_nb.Presence) - self.on_connect(self.connected) + 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 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() - self.onreceive(self._on_auth_component) - except Exception: - self.DEBUG(self.DBG,"Failed to authenticate %s" % name,'error') + def get_connect_type(self): + ''' Returns connection state. F.e.: None / 'tls' / 'plain+non_sasl'. ''' + return self.connected
- def _on_auth_component(self, data): - if data: - self.Dispatcher.ProcessNonBlocking(data) - if self.SASL.startsasl == 'in-process': - return - if self.SASL.startsasl =='success': - if self.component: - self._component = self.component - auth.NBComponentBind().PlugIn(self) - self.onreceive(_on_component_bind) - self.connected += '+sasl' - else: - raise auth.NotAuthorized(self.SASL.startsasl) + def get_peerhost(self): + ''' + Gets the ip address of the account, from which is made connection to the + server , (e.g. IP and port of gajim's socket. We will create listening socket + on the same ip + ''' + # FIXME: tuple (ip, port) is expected (and checked for) but port num is + # useless + return self.socket.peerhost
- def _on_component_bind(self, data): - if data: - self.Dispatcher.ProcessNonBlocking(data) - if self.NBComponentBind.bound is None: - return - - for domain in self.domains: - self.NBComponentBind.Bind(domain, _on_component_bound) - - def _on_component_bound(self, resp): - self.NBComponentBind.PlugOut() - - # vim: se ts=3:
Deleted: branches/osx_newbuildsys/src/common/xmpp/commands.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/commands.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/commands.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,336 +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. -""" - -import math -from protocol import * -from client import PlugIn - -DBG_COMMANDS = 'commands' - -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) - self.DBG_LINE = DBG_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='') - owner.debug_flags.append(DBG_COMMANDS) - - 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,jid=jid) - - 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 Exception: - conn.send(Error(request,ERR_BAD_REQUEST)) - raise NodeProcessed - if jid in self._handlers: - if node in self._handlers[jid]: - self._handlers[jid][node]['execute'](conn,request) - else: - conn.send(Error(request,ERR_ITEM_NOT_FOUND)) - raise NodeProcessed - elif node in self._handlers['']: - 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 jid in self._handlers: - 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 jid not in self._handlers: - self._handlers[jid]={} - self._browser.setDiscoHandler(self._DiscoHandler,node=NS_COMMANDS,jid=jid) - if name in self._handlers[jid]: - 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 jid not in self._handlers: - raise NameError,'Jid not found' - if name not in self._handlers[jid]: - 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 jid not in self._handlers: - raise NameError,'Jid not found' - elif name not in self._handlers[jid]: - 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) - 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 Exception: - session = None - try: - action = request.getTagAttr('command','action') - except Exception: - action = None - if action is None: action = 'execute' - # Check session is in session list - if session in self.sessions: - if self.sessions[session]['jid']==request.getFrom(): - # Check action is vaild - if action in self.sessions[session]['actions']: - # 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 Exception: - session = None - if session is None: - session = self.getSessionID() - self.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=self.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 Exception: - self.cmdSecondStageReply(conn,request) - if sessions[request.getTagAttr('command','sessionid')]['data']['type'] == 'circlearea': - result = num*(math.pi**2) - else: - result = num*2*math.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 self.sessions[request.getTagAttr('command','sessionid')] - - - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/debug.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/debug.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/debug.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -12,7 +12,7 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU Lesser General Public License for more details.
-""" +''' Generic debug class
Other modules can always define extra debug flags for local usage, as long as @@ -31,7 +31,7 @@
For samples of usage, see samples subdir in distro source, and selftest in this code -""" +'''
_version_ = '1.4.0'
@@ -147,7 +147,7 @@ # If you dont want to validate flags on each call to # show(), set this to 0 # - validate_flags = 1, + validate_flags = 0, # # If you dont want the welcome message, set to 0 # default is to show welcome if any flags are active @@ -202,7 +202,7 @@
def show( self, msg, flag = None, prefix = None, sufix = None, lf = 0 ): - """ + ''' flag can be of folowing types: None - this msg will always be shown if any debugging is on flag - will be shown if flag is active @@ -213,7 +213,7 @@
lf = -1 means strip linefeed if pressent lf = 1 means add linefeed if not pressent - """ + '''
if self.validate_flags: self._validate_flag( flag ) @@ -313,10 +313,10 @@
def _as_one_list( self, items ): - """ init param might contain nested lists, typically from group flags. + ''' init param might contain nested lists, typically from group flags.
This code organises lst and remves dupes - """ + ''' if not isinstance(items, (list, tuple)): return [ items ] r = [] @@ -333,7 +333,7 @@
def _append_unique_str( self, lst, item ): - """filter out any dupes.""" + '''filter out any dupes.''' if not isinstance(item, str): msg2 = '%s' % item raise Exception('Invalid item type "%s", should be string' % \ @@ -352,10 +352,10 @@ raise Exception('Invalid debugflag "%s" given' % f)
def _remove_dupe_flags( self ): - """ + ''' if multiple instances of Debug is used in same app, some flags might be created multiple time, filter out dupes - """ + ''' unique_flags = [] for f in self.debug_flags: if f not in unique_flags: @@ -364,6 +364,7 @@
colors={} def Show(self, flag, msg, prefix=''): + msg=str(msg) msg=msg.replace('\r','\r').replace('\n','\n').replace('><','>\n <') if not colors_enabled: pass elif prefix in self.colors: msg=self.colors[prefix]+msg+color_none
Deleted: branches/osx_newbuildsys/src/common/xmpp/dispatcher.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/dispatcher.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/dispatcher.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,386 +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 - -DBG_DISPATCHER = 'dispatcher' - -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) - self.DBG_LINE = DBG_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 repr(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() - owner.debug_flags.append(DBG_DISPATCHER) - - 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 xmlns not in self.handlers: self.RegisterNamespace(xmlns,'warn') - if name not in self.handlers[xmlns]: self.RegisterProtocol(name,Protocol,xmlns,'warn') - if typ+ns not in self.handlers[xmlns][name]: 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 name not in self.handlers[xmlns]: return - if typ+ns not in self.handlers[xmlns][name]: 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 xmlns not in self.handlers: - self.DEBUG("Unknown namespace: " + xmlns,'warn') - xmlns='unknown' - if name not in self.handlers[xmlns]: - 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 typ in self.handlers[xmlns][name]: list_.append(typ) # from very common... - for prop in stanza.props: - if prop in self.handlers[xmlns][name]: list_.append(prop) - if typ and typ+prop in self.handlers[xmlns][name]: 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] - - if ID in session._expected: - user=0 - if isinstance(session._expected[ID], tuple): - 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 - 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 isinstance(stanza, basestring): return self._owner_send(stanza) - if not isinstance(stanza,Protocol): _ID=None - elif not stanza.getID(): - global ID - ID+=1 - _ID=repr(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 - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/dispatcher_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/dispatcher_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/dispatcher_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -14,7 +14,6 @@ ## 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 @@ -28,16 +27,39 @@ from protocol import * from client import PlugIn
+import logging +log = logging.getLogger('gajim.c.x.dispatcher_nb') + # default timeout to wait for response for our id DEFAULT_TIMEOUT_SECONDS = 25 ID = 0
-class Dispatcher(PlugIn): +XML_DECLARATION = '<?xml version=\'1.0\'?>' + +# FIXME: ugly +class Dispatcher(): +# Why is this here - I needed to redefine Dispatcher for BOSH and easiest way +# was to inherit original Dispatcher (now renamed to XMPPDispatcher). Trouble +# is that reference used to access dispatcher instance is in Client attribute +# named by __class__.__name__ of the dispatcher instance .. long story short: +# I wrote following to avoid changing each client.Dispatcher.whatever() in xmpp/ + +# If having two kinds of dispatcher will go well, I will rewrite the dispatcher +# references in other scripts + def PlugIn(self, client_obj, after_SASL=False, old_features=None): + if client_obj.protocol_type == 'XMPP': + XMPPDispatcher().PlugIn(client_obj) + elif client_obj.protocol_type == 'BOSH': + BOSHDispatcher().PlugIn(client_obj, after_SASL, old_features) + + + +class XMPPDispatcher(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.). ''' + Can be plugged out/in to restart these headers (used for SASL f.e.). ''' + def __init__(self): PlugIn.__init__(self) - self.DBG_LINE='dispatcher' self.handlers={} self._expected={} self._defaultHandler=None @@ -47,8 +69,8 @@ self._exported_methods=[self.RegisterHandler, self.RegisterDefaultHandler, \ self.RegisterEventHandler, self.UnregisterCycleHandler, self.RegisterCycleHandler, \ self.RegisterHandlerOnce, self.UnregisterHandler, self.RegisterProtocol, \ - self.SendAndWaitForResponse, self.send,self.disconnect, \ - self.SendAndCallForResponse, self.getAnID, self.Event] + self.SendAndWaitForResponse, \ + self.SendAndCallForResponse, self.getAnID, self.Event, self.send]
def getAnID(self): global ID @@ -83,15 +105,11 @@ self._owner.lastErrNode = None self._owner.lastErr = None self._owner.lastErrCode = None - if hasattr(self._owner, 'StreamInit'): - self._owner.StreamInit() - else: - self.StreamInit() + 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() self._owner = None @@ -100,11 +118,9 @@ def StreamInit(self): ''' Send an initial stream header. ''' self.Stream = simplexml.NodeBuilder() + self.Stream.dispatch = self.dispatch 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) @@ -114,7 +130,7 @@ if locale.getdefaultlocale()[0]: self._metastream.setAttr('xml:lang', locale.getdefaultlocale()[0].split('_')[0]) - self._owner.send("<?xml version='1.0'?>%s>" % str(self._metastream)[:-2]) + self._owner.send("%s%s>" % (XML_DECLARATION,str(self._metastream)[:-2]))
def _check_stream_start(self, ns, tag, attrs): if ns!=NS_STREAMS or tag!='stream': @@ -135,11 +151,11 @@ self.Stream.Parse(data) # end stream:stream tag received if self.Stream and self.Stream.has_received_endtag(): - self._owner.Connection.disconnect() + self._owner.disconnect() return 0 except ExpatError: - self.DEBUG('Invalid XML received from server. Forcing disconnect.', 'error') - self._owner.Connection.pollend() + log.error('Invalid XML received from server. Forcing disconnect.') + self._owner.Connection.disconnect() return 0 except ValueError, e: self.DEBUG(str(e), 'error') @@ -155,7 +171,7 @@ ''' 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) + log.debug('Registering namespace "%s"' % xmlns) self.handlers[xmlns]={} self.RegisterProtocol('unknown', Protocol, xmlns=xmlns) self.RegisterProtocol('default', Protocol, xmlns=xmlns) @@ -165,8 +181,7 @@ 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) + log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns)) self.handlers[xmlns][tag_name]={type:Proto, 'default':[]}
def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', makefirst=0, system=0): @@ -193,8 +208,8 @@ ''' 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') + log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' % + (handler, name, typ, ns, xmlns)) if not typ and not ns: typ='default' if xmlns not in self.handlers: @@ -245,7 +260,7 @@ 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)) + conn._owner.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED))
def streamErrorHandler(self,conn,error): name,text='error',error.getData() @@ -281,40 +296,31 @@ def dispatch(self, stanza, session=None, direct=0): ''' Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it. Called internally. ''' + #log.info('dispatch called: stanza = %s, session = %s, direct= %s' % (stanza, session, direct)) 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': + self._owner.got_features = True session.Stream.features=stanza
xmlns=stanza.getNamespace() + + #log.info('in dispatch, getting ns for %s, and the ns is %s' % (stanza, xmlns)) if xmlns not in self.handlers: - self.DEBUG("Unknown namespace: " + xmlns, 'warn') + log.warn("Unknown namespace: " + xmlns) xmlns='unknown' + # features stanza has been handled before if name not in self.handlers[xmlns]: - self.DEBUG("Unknown stanza: " + name, 'warn') + if name != 'features': + log.warn("Unknown stanza: " + name) + else: + log.debug("Got %s/%s stanza" % (xmlns, name)) name='unknown' else: - self.DEBUG("Got %s/%s stanza" % (xmlns, name), 'ok') + log.debug("Got %s/%s stanza" % (xmlns, name))
if stanza.__class__.__name__=='Node': stanza=self.handlers[xmlns][name][type](node=stanza) @@ -324,7 +330,6 @@ 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 typ in self.handlers[xmlns][name]: list_.append(typ) # from very common... for prop in stanza.props: @@ -339,13 +344,13 @@ user=0 if isinstance(session._expected[ID], tuple): cb,args = session._expected[ID] - session.DEBUG("Expected stanza arrived. Callback %s(%s) found!" % (cb, args), 'ok') + log.debug("Expected stanza arrived. Callback %s(%s) found!" % (cb, args)) try: cb(session,stanza,**args) except Exception, typ: if typ.__class__.__name__ !='NodeProcessed': raise else: - session.DEBUG("Expected stanza arrived!",'ok') + log.debug("Expected stanza arrived!") session._expected[ID]=stanza else: user=1 @@ -401,41 +406,85 @@ Additional callback arguments can be specified in args. ''' self.SendAndWaitForResponse(stanza, 0, func, args)
- def send(self, stanza, is_message = False, now = False): - ''' Serialise stanza and put it on the wire. Assign an unique ID to it before send. - Returns assigned ID.''' - if isinstance(stanza, basestring): - return self._owner.Connection.send(stanza, now = now) - if not isinstance(stanza, Protocol): - _ID=None - elif not stanza.getID(): - global ID - ID+=1 - _ID=repr(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) - if is_message: - self._owner.Connection.send(stanza, True, now = now) - else: - self._owner.Connection.send(stanza, now = now) - return _ID + def send(self, stanza, now=False): + id = None + if type(stanza) not in [type(''), type(u'')]: + if isinstance(stanza, Protocol): + id = stanza.getID() + if id is None: + stanza.setID(self.getAnID()) + id = stanza.getID() + if self._owner._registered_name and not stanza.getAttr('from'): + stanza.setAttr('from', self._owner._registered_name) + self._owner.Connection.send(stanza, now) + return id
- def disconnect(self): +class BOSHDispatcher(XMPPDispatcher): + + def PlugIn(self, owner, after_SASL=False, old_features=None): + self.old_features = old_features + self.after_SASL = after_SASL + XMPPDispatcher.PlugIn(self, owner) + + def StreamInit(self): + ''' Send an initial stream header. ''' + self.Stream = simplexml.NodeBuilder() + self.Stream.dispatch = self.dispatch + self.Stream._dispatch_depth = 2 + self.Stream.stream_header_received = self._check_stream_start + self.Stream.features = self.old_features + + 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) + if locale.getdefaultlocale()[0]: + self._metastream.setAttr('xml:lang', + locale.getdefaultlocale()[0].split('_')[0]) + + self.restart = True + self._owner.Connection.send_init(after_SASL=self.after_SASL) + + + def StreamTerminate(self): ''' Send a stream terminator. ''' - self._owner.Connection.send('</stream:stream>') + self._owner.Connection.send_terminator()
+ def ProcessNonBlocking(self, data=None): + + if self.restart: + fromstream = self._metastream + fromstream.setAttr('from', fromstream.getAttr('to')) + fromstream.delAttr('to') + data = '%s%s>%s' % (XML_DECLARATION,str(fromstream)[:-2] ,data) + self.restart = False + + return XMPPDispatcher.ProcessNonBlocking(self, data) + + def dispatch(self, stanza, session=None, direct=0): + + if stanza.getName()=='body' and stanza.getNamespace()==NS_HTTP_BIND: + + stanza_attrs = stanza.getAttrs() + if stanza_attrs.has_key('authid'): + # should be only in init response + # auth module expects id of stream in document attributes + self.Stream._document_attrs['id'] = stanza_attrs['authid'] + + self._owner.Connection.handle_body_attrs(stanza_attrs) + + children = stanza.getChildren() + + if children: + for child in children: + # if child doesn't have any ns specified, simplexml (or expat) thinks it's + # of parent's (BOSH body) namespace, so we have to rewrite it to + # jabber:client + if child.getNamespace() == NS_HTTP_BIND: + child.setNamespace(self._owner.defaultNamespace) + XMPPDispatcher.dispatch(self, child, session, direct) + else: + XMPPDispatcher.dispatch(self, stanza, session, direct) + # vim: se ts=3:
Deleted: branches/osx_newbuildsys/src/common/xmpp/features.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/features.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/features.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,188 +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 not isinstance(info, dict): 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 Exception: - 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 Exception: - 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 - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/features_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/features_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/features_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -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): @@ -28,10 +32,10 @@ disp.SendAndCallForResponse(iq, _on_response)
def _discover(disp, ns, jid, node = None, fb2b=0, fb2a=1, cb=None): - """ Try to obtain info from the remote object. + ''' 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. """ + (if gb2a is true). Returns obtained info. Used internally. ''' iq=Iq(to=jid, typ='get', queryNS=ns) if node: iq.setQuerynode(node) @@ -57,7 +61,7 @@
# this function is not used in gajim ??? def discoverItems(disp,jid,node=None, cb=None): - """ Query remote object about any items that it contains. Return items list. """ + ''' 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. @@ -74,7 +78,7 @@
# this one is def discoverInfo(disp,jid,node=None, cb=None): - """ Query remote object about info that it publishes. Returns identities and features lists.""" + ''' 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. @@ -104,11 +108,11 @@
### Registration ### jabber:iq:register ### JEP-0077 ########################### def getRegInfo(disp, host, info={}, sync=True): - """ Gets registration form from remote host. + ''' 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.""" + 'disp' must be connected dispatcher instance.''' iq=Iq('get',NS_REGISTER,to=host) for i in info.keys(): iq.setTagData(i,info[i]) @@ -140,11 +144,11 @@ con.Event(NS_REGISTER, REGISTER_DATA_RECEIVED, (agent,df,False,''))
def register(disp, host, info, cb): - """ Perform registration on remote server with provided info. + ''' Perform registration on remote server with provided info. disp must be connected dispatcher instance. 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 not isinstance(info, dict): info=info.asDict() @@ -153,16 +157,16 @@ disp.SendAndCallForResponse(iq, cb)
def unregister(disp, host, cb): - """ Unregisters with host (permanently removes account). + ''' Unregisters with host (permanently removes account). disp must be connected and authorized dispatcher instance. - Returns true on success.""" + Returns true on success.''' iq = Iq('set', NS_REGISTER, to=host, payload=[Node('remove')]) _on_default_response(disp, iq, cb)
def changePasswordTo(disp, newpassword, host=None, cb = None): - """ Changes password on specified or current (if not specified) server. + ''' Changes password on specified or current (if not specified) server. disp must be connected and authorized dispatcher instance. - Returns true on success.""" + Returns true on success.''' if not host: host=disp._owner.Server iq = Iq('set',NS_REGISTER,to=host, payload=[Node('username', payload=[disp._owner.Server]),Node('password',payload=[newpassword])]) @@ -173,8 +177,8 @@ #action=[allow|deny]
def getPrivacyLists(disp): - """ Requests privacy lists from connected server. - Returns dictionary of existing lists on success.""" + ''' Requests privacy lists from connected server. + Returns dictionary of existing lists on success.''' iq = Iq('get', NS_PRIVACY) def _on_response(resp): dict_ = {'lists': []} @@ -205,8 +209,8 @@ disp.SendAndCallForResponse(iq, _on_response)
def getPrivacyList(disp, listname): - """ Requests specific privacy list listname. Returns list of XML nodes (rules) - taken from the server responce.""" + ''' Requests specific privacy list listname. Returns list of XML nodes (rules) + taken from the server responce.''' def _on_response(resp): if not isResultNode(resp): disp.Event(NS_PRIVACY, PRIVACY_LIST_RECEIVED, (False)) @@ -216,8 +220,8 @@ disp.SendAndCallForResponse(iq, _on_response)
def setActivePrivacyList(disp, listname=None, typ='active', cb=None): - """ Switches privacy list 'listname' to specified type. - By default the type is 'active'. Returns true on success.""" + ''' Switches privacy list 'listname' to specified type. + By default the type is 'active'. Returns true on success.''' if listname: attrs={'name':listname} else: @@ -226,13 +230,13 @@ _on_default_response(disp, iq, cb)
def setDefaultPrivacyList(disp, listname=None): - """ Sets the default privacy list as 'listname'. Returns true on success.""" + ''' Sets the default privacy list as 'listname'. Returns true on success.''' return setActivePrivacyList(disp, listname,'default')
def setPrivacyList(disp, listname, tags): - """ Set the ruleset. 'list' should be the simpleXML node formatted + ''' 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.""" + Returns true on success.''' iq = Iq('set', NS_PRIVACY, xmlns = '') list_query = iq.getTag('query').setTag('list', {'name': listname}) for item in tags: @@ -248,7 +252,7 @@ _on_default_response(disp, iq, None)
def delPrivacyList(disp,listname,cb=None): - """ Deletes privacy list 'listname'. Returns true on success.""" + ''' Deletes privacy list 'listname'. Returns true on success.''' iq = Iq('set',NS_PRIVACY,payload=[Node('list',{'name':listname})]) _on_default_response(disp, iq, cb)
Deleted: branches/osx_newbuildsys/src/common/xmpp/filetransfer.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/filetransfer.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/filetransfer.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,204 +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%27/%3E - # </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 Exception: - 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]=='|>': - pass - 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 Exception: - 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)) - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/idlequeue.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/idlequeue.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/idlequeue.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -13,6 +13,8 @@ ## GNU General Public License for more details.
import select +import logging +log = logging.getLogger('gajim.c.x.idlequeue')
class IdleObject: ''' base class for all idle listeners, these are the methods, which are called from IdleQueue @@ -32,8 +34,8 @@ ''' called on new write event (connect in sockets is a pollout) ''' pass
- def read_timeout(self, fd): - ''' called when timeout has happend ''' + def read_timeout(self): + ''' called when timeout happened ''' pass
class IdleQueue: @@ -52,6 +54,7 @@ self.selector = select.poll()
def remove_timeout(self, fd): + log.info('read timeout removed for fd %s' % fd) if fd in self.read_timeouts: del(self.read_timeouts[fd])
@@ -63,19 +66,38 @@ self.alarms[alarm_time].append(alarm_cb) else: self.alarms[alarm_time] = [alarm_cb] + return alarm_time
+ def remove_alarm(self, alarm_cb, alarm_time): + ''' removes alarm callback alarm_cb scheduled on alarm_time''' + if not self.alarms.has_key(alarm_time): return False + i = -1 + for i in range(len(self.alarms[alarm_time])): + # let's not modify the list inside the loop + if self.alarms[alarm_time][i] is alarm_cb: break + if i != -1: + del self.alarms[alarm_time][i] + if self.alarms[alarm_time] == []: + del self.alarms[alarm_time] + return True + else: + return False + def set_read_timeout(self, fd, seconds): ''' set a new timeout, if it is not removed after 'seconds', then obj.read_timeout() will be called ''' + log.info('read timeout set for fd %s on %s seconds' % (fd, seconds)) timeout = self.current_time() + seconds self.read_timeouts[fd] = timeout
def check_time_events(self): + log.info('check time evs') current_time = self.current_time() for fd, timeout in self.read_timeouts.items(): if timeout > current_time: continue if fd in self.queue: + log.debug('Calling read_timeout for fd %s' % fd) self.queue[fd].read_timeout() else: self.remove_timeout(fd) @@ -83,9 +105,9 @@ for alarm_time in times: if alarm_time > current_time: break - for cb in self.alarms[alarm_time]: - cb() - del(self.alarms[alarm_time]) + if self.alarms.has_key(alarm_time): + for cb in self.alarms[alarm_time]: cb() + if self.alarms.has_key(alarm_time): del(self.alarms[alarm_time])
def plug_idle(self, obj, writable = True, readable = True): if obj.fd == -1: @@ -128,6 +150,7 @@ return False
if flags & 3: # waiting read event + #print 'waiting read on %d, flags are %d' % (fd, flags) obj.pollin() return True
Modified: branches/osx_newbuildsys/src/common/xmpp/protocol.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/protocol.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/protocol.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -14,109 +14,109 @@
# $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $
-""" +''' Protocol module contains tools that is needed for processing of xmpp-related data structures. -""" +'''
from simplexml import Node, NodeBuilder import time -NS_ACTIVITY ='http://jabber.org/protocol/activity' # XEP-0108 -NS_ADDRESS ='http://jabber.org/protocol/address' # XEP-0033 -NS_AGENTS ='jabber:iq:agents' -NS_AMP ='http://jabber.org/protocol/amp' +NS_ACTIVITY ='http://jabber.org/protocol/activity' # XEP-0108 +NS_ADDRESS ='http://jabber.org/protocol/address' # XEP-0033 +NS_AGENTS ='jabber:iq:agents' +NS_AMP ='http://jabber.org/protocol/amp' NS_AMP_ERRORS =NS_AMP+'#errors' -NS_AUTH ='jabber:iq:auth' -NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata' -NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind' -NS_BROWSE ='jabber:iq:browse' -NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195 -NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065 -NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115 -NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085 -NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194 -NS_CLIENT ='jabber:client' -NS_COMMANDS ='http://jabber.org/protocol/commands' +NS_AUTH ='jabber:iq:auth' +NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata' +NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind' +NS_BROWSE ='jabber:iq:browse' +NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195 +NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065 +NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115 +NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085 +NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194 +NS_CLIENT ='jabber:client' +NS_COMMANDS ='http://jabber.org/protocol/commands' NS_COMPONENT_ACCEPT='jabber:component:accept' NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0' -NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138 +NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138 NS_CONFERENCE ='jabber:x:conference' -NS_DATA ='jabber:x:data' # XEP-0004 -NS_DELAY ='jabber:x:delay' -NS_DELAY2 ='urn:xmpp:delay' -NS_DIALBACK ='jabber:server:dialback' -NS_DISCO ='http://jabber.org/protocol/disco' +NS_DATA ='jabber:x:data' # XEP-0004 +NS_DELAY ='jabber:x:delay' +NS_DELAY2 ='urn:xmpp:delay' +NS_DIALBACK ='jabber:server:dialback' +NS_DISCO ='http://jabber.org/protocol/disco' NS_DISCO_INFO =NS_DISCO+'#info' NS_DISCO_ITEMS =NS_DISCO+'#items' -NS_ENCRYPTED ='jabber:x:encrypted' # XEP-0027 -NS_ESESSION ='http://www.xmpp.org/extensions/xep-0116.html#ns' +NS_ENCRYPTED ='jabber:x:encrypted' # XEP-0027 +NS_ESESSION ='http://www.xmpp.org/extensions/xep-0116.html#ns' NS_ESESSION_INIT='http://www.xmpp.org/extensions/xep-0116.html#ns-init' # XEP-0116 -NS_EVENT ='jabber:x:event' # XEP-0022 -NS_FEATURE ='http://jabber.org/protocol/feature-neg' -NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 -NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196 -NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080 -NS_GROUPCHAT ='gc-1.0' -NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070 -NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124 -NS_IBB ='http://jabber.org/protocol/ibb' -NS_INVISIBLE ='presence-invisible' # Jabberd2 -NS_IQ ='iq' # Jabberd2 -NS_LAST ='jabber:iq:last' -NS_MESSAGE ='message' # Jabberd2 -NS_MOOD ='http://jabber.org/protocol/mood' # XEP-0107 -NS_MUC ='http://jabber.org/protocol/muc' -NS_MUC_USER =NS_MUC+'#user' -NS_MUC_ADMIN =NS_MUC+'#admin' -NS_MUC_OWNER =NS_MUC+'#owner' +NS_EVENT ='jabber:x:event' # XEP-0022 +NS_FEATURE ='http://jabber.org/protocol/feature-neg' +NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 +NS_GAMING ='http://jabber.org/protocol/gaming' # XEP-0196 +NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080 +NS_GROUPCHAT ='gc-1.0' +NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # XEP-0070 +NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124 +NS_IBB ='http://jabber.org/protocol/ibb' +NS_INVISIBLE ='presence-invisible' # Jabberd2 +NS_IQ ='iq' # Jabberd2 +NS_LAST ='jabber:iq:last' +NS_MESSAGE ='message' # Jabberd2 +NS_MOOD ='http://jabber.org/protocol/mood' # XEP-0107 +NS_MUC ='http://jabber.org/protocol/muc' +NS_MUC_USER =NS_MUC+'#user' +NS_MUC_ADMIN =NS_MUC+'#admin' +NS_MUC_OWNER =NS_MUC+'#owner' NS_MUC_UNIQUE =NS_MUC+'#unique' NS_MUC_CONFIG =NS_MUC+'#roomconfig' -NS_NICK ='http://jabber.org/protocol/nick' # XEP-0172 -NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # XEP-0013 -NS_PHYSLOC ='http://jabber.org/protocol/physloc' # XEP-0112 -NS_PING ='urn:xmpp:ping' # SEP-0199 -NS_PRESENCE ='presence' # Jabberd2 -NS_PRIVACY ='jabber:iq:privacy' -NS_PRIVATE ='jabber:iq:private' -NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154 -NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060 -NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060 -NS_REGISTER ='jabber:iq:register' -NS_ROSTER ='jabber:iq:roster' -NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144 -NS_RPC ='jabber:iq:rpc' # XEP-0009 -NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl' -NS_SEARCH ='jabber:iq:search' -NS_SERVER ='jabber:server' -NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' -NS_SI ='http://jabber.org/protocol/si' # XEP-0096 -NS_SI_PUB ='http://jabber.org/protocol/sipub' # XEP-0137 -NS_SIGNED ='jabber:x:signed' # XEP-0027 -NS_SSN ='urn:xmpp:ssn' # XEP-0155 -NS_STANZA_CRYPTO='http://www.xmpp.org/extensions/xep-0200.html#ns' # XEP-0200 -NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas' -NS_STREAM ='http://affinix.com/jabber/stream' -NS_STREAMS ='http://etherx.jabber.org/streams' -NS_TIME ='jabber:iq:time' # XEP-0900 -NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202 -NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' -NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118 -NS_VACATION ='http://jabber.org/protocol/vacation' -NS_VCARD ='vcard-temp' +NS_NICK ='http://jabber.org/protocol/nick' # XEP-0172 +NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # XEP-0013 +NS_PHYSLOC ='http://jabber.org/protocol/physloc' # XEP-0112 +NS_PING ='urn:xmpp:ping' # SEP-0199 +NS_PRESENCE ='presence' # Jabberd2 +NS_PRIVACY ='jabber:iq:privacy' +NS_PRIVATE ='jabber:iq:private' +NS_PROFILE ='http://jabber.org/protocol/profile' # XEP-0154 +NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060 +NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner' # JEP-0060 +NS_REGISTER ='jabber:iq:register' +NS_ROSTER ='jabber:iq:roster' +NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144 +NS_RPC ='jabber:iq:rpc' # XEP-0009 +NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl' +NS_SEARCH ='jabber:iq:search' +NS_SERVER ='jabber:server' +NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' +NS_SI ='http://jabber.org/protocol/si' # XEP-0096 +NS_SI_PUB ='http://jabber.org/protocol/sipub' # XEP-0137 +NS_SIGNED ='jabber:x:signed' # XEP-0027 +NS_SSN ='urn:xmpp:ssn' # XEP-0155 +NS_STANZA_CRYPTO='http://www.xmpp.org/extensions/xep-0200.html#ns' # XEP-0200 +NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas' +NS_STREAM ='http://affinix.com/jabber/stream' +NS_STREAMS ='http://etherx.jabber.org/streams' +NS_TIME ='jabber:iq:time' # XEP-0900 +NS_TIME_REVISED ='urn:xmpp:time' # XEP-0202 +NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' +NS_TUNE ='http://jabber.org/protocol/tune' # XEP-0118 +NS_VACATION ='http://jabber.org/protocol/vacation' +NS_VCARD ='vcard-temp' NS_GMAILNOTIFY ='google:mail:notify' NS_GTALKSETTING ='google:setting' NS_VCARD_UPDATE =NS_VCARD+':x:update' -NS_VERSION ='jabber:iq:version' -NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197 -NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130 -NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071 -NS_XHTML = 'http://www.w3.org/1999/xhtml' # " -NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141 -NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122 +NS_VERSION ='jabber:iq:version' +NS_VIEWING ='http://jabber.org/protocol/viewing' # XEP--197 +NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130 +NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071 +NS_XHTML = 'http://www.w3.org/1999/xhtml' # " +NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141 +NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # XEP-0122 NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' NS_RECEIPTS ='urn:xmpp:receipts'
-xmpp_stream_error_conditions=""" +xmpp_stream_error_conditions=''' bad-format -- -- -- The entity has sent XML that cannot be processed. bad-namespace-prefix -- -- -- The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix. conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream. @@ -140,8 +140,8 @@ unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server. unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server. unsupported-version -- -- -- The value of the 'version' attribute provided by the initiating entity in the stream header specifies a version of XMPP that is not supported by the server. -xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed.""" -xmpp_stanza_error_conditions=""" +xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed.''' +xmpp_stanza_error_conditions=''' bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed. conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address. feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed. @@ -163,39 +163,39 @@ service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service. subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required. undefined-condition -- 500 -- -- Undefined Condition -unexpected-request -- 400 -- wait -- The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).""" -sasl_error_conditions=""" +unexpected-request -- 400 -- wait -- The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).''' +sasl_error_conditions=''' aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element. incorrect-encoding -- -- -- The data provided by the initiating entity could not be processed because the [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003. encoding is incorrect (e.g., because the encoding does not adhere to the definition in Section 3 of [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003.); sent in reply to a <response/> element or an <auth/> element with initial response data. invalid-authzid -- -- -- The authzid provided by the initiating entity is invalid, either because it is incorrectly formatted or because the initiating entity does not have permissions to authorize that ID; sent in reply to a <response/> element or an <auth/> element with initial response data. invalid-mechanism -- -- -- The initiating entity did not provide a mechanism or requested a mechanism that is not supported by the receiving entity; sent in reply to an <auth/> element. mechanism-too-weak -- -- -- The mechanism requested by the initiating entity is weaker than server policy permits for that initiating entity; sent in reply to a <response/> element or an <auth/> element with initial response data. not-authorized -- -- -- The authentication failed because the initiating entity did not provide valid credentials (this includes but is not limited to the case of an unknown username); sent in reply to a <response/> element or an <auth/> element with initial response data. -temporary-auth-failure -- -- -- The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/> element or <response/> element.""" +temporary-auth-failure -- -- -- The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/> element or <response/> element.'''
ERRORS,_errorcodes={},{} for ns,errname,errpool in ((NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_conditions), - (NS_STANZAS ,'ERR' ,xmpp_stanza_error_conditions), - (NS_SASL ,'SASL' ,sasl_error_conditions)): - for err in errpool.split('\n')[1:]: - cond,code,typ,text=err.split(' -- ') - name=errname+'_'+cond.upper().replace('-','_') - locals()[name]=ns+' '+cond - ERRORS[ns+' '+cond]=[code,typ,text] - if code: _errorcodes[code]=cond + (NS_STANZAS ,'ERR' ,xmpp_stanza_error_conditions), + (NS_SASL ,'SASL' ,sasl_error_conditions)): + for err in errpool.split('\n')[1:]: + cond,code,typ,text=err.split(' -- ') + name=errname+'_'+cond.upper().replace('-','_') + locals()[name]=ns+' '+cond + ERRORS[ns+' '+cond]=[code,typ,text] + if code: _errorcodes[code]=cond del ns,errname,errpool,err,cond,code,typ,text
def isResultNode(node): - """ Returns true if the node is a positive reply. """ - return node and node.getType()=='result' + ''' Returns true if the node is a positive reply. ''' + return node and node.getType()=='result' def isErrorNode(node): - """ Returns true if the node is a negative reply. """ - return node and node.getType()=='error' + ''' Returns true if the node is a negative reply. ''' + return node and node.getType()=='error'
class NodeProcessed(Exception): - """ Exception that should be raised by handler when the handling should be stopped. """ + ''' Exception that should be raised by handler when the handling should be stopped. ''' class StreamError(Exception): - """ Base exception class for stream errors.""" + ''' Base exception class for stream errors.''' class BadFormat(StreamError): pass class BadNamespacePrefix(StreamError): pass class Conflict(StreamError): pass @@ -222,582 +222,589 @@ class XMLNotWellFormed(StreamError): pass
stream_exceptions = {'bad-format': BadFormat, - 'bad-namespace-prefix': BadNamespacePrefix, - 'conflict': Conflict, - 'connection-timeout': ConnectionTimeout, - 'host-gone': HostGone, - 'host-unknown': HostUnknown, - 'improper-addressing': ImproperAddressing, - 'internal-server-error': InternalServerError, - 'invalid-from': InvalidFrom, - 'invalid-id': InvalidID, - 'invalid-namespace': InvalidNamespace, - 'invalid-xml': InvalidXML, - 'not-authorized': NotAuthorized, - 'policy-violation': PolicyViolation, - 'remote-connection-failed': RemoteConnectionFailed, - 'resource-constraint': ResourceConstraint, - 'restricted-xml': RestrictedXML, - 'see-other-host': SeeOtherHost, - 'system-shutdown': SystemShutdown, - 'undefined-condition': UndefinedCondition, - 'unsupported-encoding': UnsupportedEncoding, - 'unsupported-stanza-type': UnsupportedStanzaType, - 'unsupported-version': UnsupportedVersion, - 'xml-not-well-formed': XMLNotWellFormed} + 'bad-namespace-prefix': BadNamespacePrefix, + 'conflict': Conflict, + 'connection-timeout': ConnectionTimeout, + 'host-gone': HostGone, + 'host-unknown': HostUnknown, + 'improper-addressing': ImproperAddressing, + 'internal-server-error': InternalServerError, + 'invalid-from': InvalidFrom, + 'invalid-id': InvalidID, + 'invalid-namespace': InvalidNamespace, + 'invalid-xml': InvalidXML, + 'not-authorized': NotAuthorized, + 'policy-violation': PolicyViolation, + 'remote-connection-failed': RemoteConnectionFailed, + 'resource-constraint': ResourceConstraint, + 'restricted-xml': RestrictedXML, + 'see-other-host': SeeOtherHost, + 'system-shutdown': SystemShutdown, + 'undefined-condition': UndefinedCondition, + 'unsupported-encoding': UnsupportedEncoding, + 'unsupported-stanza-type': UnsupportedStanzaType, + 'unsupported-version': UnsupportedVersion, + 'xml-not-well-formed': XMLNotWellFormed}
class JID: - """ JID object. JID can be built from string, modified, compared, serialised into string. """ - def __init__(self, jid=None, node='', domain='', resource=''): - """ Constructor. JID can be specified as string (jid argument) or as separate parts. - Examples: - JID('node@domain/resource') - JID(node='node',domain='domain.org') - """ - if not jid and not domain: raise ValueError('JID must contain at least domain name') - elif isinstance(jid, type(self)): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource - elif domain: self.node,self.domain,self.resource=node,domain,resource - else: - if jid.find('@')+1: self.node,jid=jid.split('@',1) - else: self.node='' - if jid.find('/')+1: self.domain,self.resource=jid.split('/',1) - else: self.domain,self.resource=jid,'' - def getNode(self): - """ Return the node part of the JID """ - return self.node - def setNode(self,node): - """ Set the node part of the JID to new value. Specify None to remove the node part.""" - self.node=node.lower() - def getDomain(self): - """ Return the domain part of the JID """ - return self.domain - def setDomain(self,domain): - """ Set the domain part of the JID to new value.""" - self.domain=domain.lower() - def getResource(self): - """ Return the resource part of the JID """ - return self.resource - def setResource(self,resource): - """ Set the resource part of the JID to new value. Specify None to remove the resource part.""" - self.resource=resource - def getStripped(self): - """ Return the bare representation of JID. I.e. string value w/o resource. """ - return self.__str__(0) - def __eq__(self, other): - """ Compare the JID to another instance or to string for equality. """ - try: other=JID(other) - except ValueError: return 0 - return self.resource==other.resource and self.__str__(0) == other.__str__(0) - def __ne__(self, other): - """ Compare the JID to another instance or to string for non-equality. """ - return not self.__eq__(other) - def bareMatch(self, other): - """ Compare the node and domain parts of the JID's for equality. """ - return self.__str__(0) == JID(other).__str__(0) - def __str__(self,wresource=1): - """ Serialise JID into string. """ - if self.node: jid=self.node+'@'+self.domain - else: jid=self.domain - if wresource and self.resource: return jid+'/'+self.resource - return jid - def __hash__(self): - """ Produce hash of the JID, Allows to use JID objects as keys of the dictionary. """ - return hash(self.__str__()) + ''' JID object. JID can be built from string, modified, compared, serialised into string. ''' + def __init__(self, jid=None, node='', domain='', resource=''): + ''' Constructor. JID can be specified as string (jid argument) or as separate parts. + Examples: + JID('node@domain/resource') + JID(node='node',domain='domain.org') + ''' + if not jid and not domain: raise ValueError('JID must contain at least domain name') + elif type(jid)==type(self): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource + elif domain: self.node,self.domain,self.resource=node,domain,resource + else: + if jid.find('@')+1: self.node,jid=jid.split('@',1) + else: self.node='' + if jid.find('/')+1: self.domain,self.resource=jid.split('/',1) + else: self.domain,self.resource=jid,'' + def getNode(self): + ''' Return the node part of the JID ''' + return self.node + def setNode(self,node): + ''' Set the node part of the JID to new value. Specify None to remove the node part.''' + self.node=node.lower() + def getDomain(self): + ''' Return the domain part of the JID ''' + return self.domain + def setDomain(self,domain): + ''' Set the domain part of the JID to new value.''' + self.domain=domain.lower() + def getResource(self): + ''' Return the resource part of the JID ''' + return self.resource + def setResource(self,resource): + ''' Set the resource part of the JID to new value. Specify None to remove the resource part.''' + self.resource=resource + def getStripped(self): + ''' Return the bare representation of JID. I.e. string value w/o resource. ''' + return self.__str__(0) + def __eq__(self, other): + ''' Compare the JID to another instance or to string for equality. ''' + try: other=JID(other) + except ValueError: return 0 + return self.resource==other.resource and self.__str__(0) == other.__str__(0) + def __ne__(self, other): + ''' Compare the JID to another instance or to string for non-equality. ''' + return not self.__eq__(other) + def bareMatch(self, other): + ''' Compare the node and domain parts of the JID's for equality. ''' + return self.__str__(0) == JID(other).__str__(0) + def __str__(self,wresource=1): + ''' Serialise JID into string. ''' + if self.node: jid=self.node+'@'+self.domain + else: jid=self.domain + if wresource and self.resource: return jid+'/'+self.resource + return jid + def __hash__(self): + ''' Produce hash of the JID, Allows to use JID objects as keys of the dictionary. ''' + return hash(self.__str__())
+class BOSHBody(Node): + ''' + <body> tag that wraps usual XMPP stanzas in XMPP over BOSH + ''' + def __init__(self, attrs={}, payload=[], node=None): + Node.__init__(self, tag='body', attrs=attrs, payload=payload, node=node) + self.setNamespace(NS_HTTP_BIND) + + class Protocol(Node): - """ A "stanza" object class. Contains methods that are common for presences, iqs and messages. """ - def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None): - """ Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'. - to is the value of 'to' attribure, 'typ' - 'type' attribute - frn - from attribure, attrs - other attributes mapping, payload - same meaning as for simplexml payload definition - timestamp - the time value that needs to be stamped over stanza - xmlns - namespace of top stanza node - node - parsed or unparsed stana to be taken as prototype. - """ - if not attrs: attrs={} - if to: attrs['to']=to - if frm: attrs['from']=frm - if typ: attrs['type']=typ - Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node) - if not node and xmlns: self.setNamespace(xmlns) - if self['to']: self.setTo(self['to']) - if self['from']: self.setFrom(self['from']) - if node and isinstance(self, type(node)) and self.__class__==node.__class__ and 'id' in self.attrs: del self.attrs['id'] - self.timestamp=None - for d in self.getTags('delay',namespace=NS_DELAY2): - try: - if d.getAttr('stamp') < self.getTimestamp2(): - self.setTimestamp(d.getAttr('stamp')) - except Exception: - pass - if not self.timestamp: - for x in self.getTags('x',namespace=NS_DELAY): - try: - if x.getAttr('stamp') < self.getTimestamp(): - self.setTimestamp(x.getAttr('stamp')) - except Exception: - pass - if timestamp is not None: self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp='' - def getTo(self): - """ Return value of the 'to' attribute. """ - try: - return self['to'] - except KeyError: - return None - def getFrom(self): - """ Return value of the 'from' attribute. """ - try: - return self['from'] - except KeyError: - return None - def getTimestamp(self): - """ Return the timestamp in the 'yyyymmddThhmmss' format. """ - if self.timestamp: return self.timestamp - return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) - def getTimestamp2(self): - """ Return the timestamp in the 'yyyymmddThhmmss' format. """ - if self.timestamp: return self.timestamp - return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) - def getID(self): - """ Return the value of the 'id' attribute. """ - return self.getAttr('id') - def setTo(self,val): - """ Set the value of the 'to' attribute. """ - self.setAttr('to', JID(val)) - def getType(self): - """ Return the value of the 'type' attribute. """ - return self.getAttr('type') - def setFrom(self,val): - """ Set the value of the 'from' attribute. """ - self.setAttr('from', JID(val)) - def setType(self,val): - """ Set the value of the 'type' attribute. """ - self.setAttr('type', val) - def setID(self,val): - """ Set the value of the 'id' attribute. """ - self.setAttr('id', val) - def getError(self): - """ Return the error-condition (if present) or the textual description of the error (otherwise). """ - errtag=self.getTag('error') - if errtag: - for tag in errtag.getChildren(): - if tag.getName()!='text': return tag.getName() - return errtag.getData() - def getErrorMsg(self): - """ Return the textual description of the error (if present) or the error condition """ - errtag=self.getTag('error') - if errtag: - for tag in errtag.getChildren(): - if tag.getName()=='text': return tag.getData() - return self.getError() - def getErrorCode(self): - """ Return the error code. Obsolete. """ - return self.getTagAttr('error','code') - def setError(self,error,code=None): - """ Set the error code. Obsolete. Use error-conditions instead. """ - if code: - if str(code) in _errorcodes.keys(): error=ErrorNode(_errorcodes[str(code)],text=error) - else: error=ErrorNode(ERR_UNDEFINED_CONDITION,code=code,typ='cancel',text=error) - elif isinstance(error, basestring): error=ErrorNode(error) - self.setType('error') - self.addChild(node=error) - def setTimestamp(self,val=None): - """Set the timestamp. timestamp should be the yyyymmddThhmmss string.""" - if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) - self.timestamp=val - self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY) - def getProperties(self): - """ Return the list of namespaces to which belongs the direct childs of element""" - props=[] - for child in self.getChildren(): - prop=child.getNamespace() - if prop not in props: props.append(prop) - return props - def __setitem__(self,item,val): - """ Set the item 'item' to the value 'val'.""" - if item in ['to','from']: val=JID(val) - return self.setAttr(item,val) + ''' A "stanza" object class. Contains methods that are common for presences, iqs and messages. ''' + def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None): + ''' Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'. + to is the value of 'to' attribure, 'typ' - 'type' attribute + frn - from attribure, attrs - other attributes mapping, + payload - same meaning as for simplexml payload definition + timestamp - the time value that needs to be stamped over stanza + xmlns - namespace of top stanza node + node - parsed or unparsed stana to be taken as prototype. + ''' + if not attrs: attrs={} + if to: attrs['to']=to + if frm: attrs['from']=frm + if typ: attrs['type']=typ + Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node) + if not node and xmlns: self.setNamespace(xmlns) + if self['to']: self.setTo(self['to']) + if self['from']: self.setFrom(self['from']) + if node and type(self)==type(node) and self.__class__==node.__class__ and self.attrs.has_key('id'): del self.attrs['id'] + self.timestamp=None + for d in self.getTags('delay',namespace=NS_DELAY2): + try: + if d.getAttr('stamp') < self.getTimestamp2(): + self.setTimestamp(d.getAttr('stamp')) + except Exception: + pass + if not self.timestamp: + for x in self.getTags('x',namespace=NS_DELAY): + try: + if x.getAttr('stamp') < self.getTimestamp(): + self.setTimestamp(x.getAttr('stamp')) + except Exception: + pass + if timestamp is not None: self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp='' + def getTo(self): + ''' Return value of the 'to' attribute. ''' + try: return self['to'] + except: return None + def getFrom(self): + ''' Return value of the 'from' attribute. ''' + try: return self['from'] + except: return None + def getTimestamp(self): + ''' Return the timestamp in the 'yyyymmddThhmmss' format. ''' + if self.timestamp: return self.timestamp + return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) + def getTimestamp2(self): + """ Return the timestamp in the 'yyyymmddThhmmss' format. """ + if self.timestamp: return self.timestamp + return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) + def getID(self): + ''' Return the value of the 'id' attribute. ''' + return self.getAttr('id') + def setTo(self,val): + ''' Set the value of the 'to' attribute. ''' + self.setAttr('to', JID(val)) + def getType(self): + ''' Return the value of the 'type' attribute. ''' + return self.getAttr('type') + def setFrom(self,val): + ''' Set the value of the 'from' attribute. ''' + self.setAttr('from', JID(val)) + def setType(self,val): + ''' Set the value of the 'type' attribute. ''' + self.setAttr('type', val) + def setID(self,val): + ''' Set the value of the 'id' attribute. ''' + self.setAttr('id', val) + def getError(self): + ''' Return the error-condition (if present) or the textual description of the error (otherwise). ''' + errtag=self.getTag('error') + if errtag: + for tag in errtag.getChildren(): + if tag.getName()<>'text': return tag.getName() + return errtag.getData() + def getErrorMsg(self): + ''' Return the textual description of the error (if present) or the error condition ''' + errtag=self.getTag('error') + if errtag: + for tag in errtag.getChildren(): + if tag.getName()=='text': return tag.getData() + return self.getError() + def getErrorCode(self): + ''' Return the error code. Obsolete. ''' + return self.getTagAttr('error','code') + def setError(self,error,code=None): + ''' Set the error code. Obsolete. Use error-conditions instead. ''' + if code: + if str(code) in _errorcodes.keys(): error=ErrorNode(_errorcodes[str(code)],text=error) + else: error=ErrorNode(ERR_UNDEFINED_CONDITION,code=code,typ='cancel',text=error) + elif type(error) in [type(''),type(u'')]: error=ErrorNode(error) + self.setType('error') + self.addChild(node=error) + def setTimestamp(self,val=None): + '''Set the timestamp. timestamp should be the yyyymmddThhmmss string.''' + if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) + self.timestamp=val + self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY) + def getProperties(self): + ''' Return the list of namespaces to which belongs the direct childs of element''' + props=[] + for child in self.getChildren(): + prop=child.getNamespace() + if prop not in props: props.append(prop) + return props + def __setitem__(self,item,val): + ''' Set the item 'item' to the value 'val'.''' + if item in ['to','from']: val=JID(val) + return self.setAttr(item,val)
+ class Message(Protocol): - """ XMPP Message stanza - "push" mechanism.""" - def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None): - """ Create message object. You can specify recipient, text of message, type of message - any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go. - Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. """ - Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) - if body: self.setBody(body) - if xhtml: self.setXHTML(xhtml) - if subject is not None: self.setSubject(subject) - def getBody(self): - """ Returns text of the message. """ - return self.getTagData('body') - def getXHTML(self, xmllang=None): - """ Returns serialized xhtml-im element text of the message. + ''' XMPP Message stanza - "push" mechanism.''' + def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None): + ''' Create message object. You can specify recipient, text of message, type of message + any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go. + Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. ''' + Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) + if body: self.setBody(body) + if xhtml: self.setXHTML(xhtml) + if subject is not None: self.setSubject(subject) + def getBody(self): + ''' Returns text of the message. ''' + return self.getTagData('body') + def getXHTML(self, xmllang=None): + ''' Returns serialized xhtml-im element text of the message.
- TODO: Returning a DOM could make rendering faster.""" - xhtml = self.getTag('html') - if xhtml: - if xmllang: - body = xhtml.getTag('body', attrs={'xml:lang':xmllang}) - else: - body = xhtml.getTag('body') - return str(body) - return None - def getSubject(self): - """ Returns subject of the message. """ - return self.getTagData('subject') - def getThread(self): - """ Returns thread of the message. """ - return self.getTagData('thread') - def setBody(self,val): - """ Sets the text of the message. """ - self.setTagData('body',val) + TODO: Returning a DOM could make rendering faster.''' + xhtml = self.getTag('html') + if xhtml: + if xmllang: + body = xhtml.getTag('body', attrs={'xml:lang':xmllang}) + else: + body = xhtml.getTag('body') + return str(body) + return None + def getSubject(self): + ''' Returns subject of the message. ''' + return self.getTagData('subject') + def getThread(self): + ''' Returns thread of the message. ''' + return self.getTagData('thread') + def setBody(self,val): + ''' Sets the text of the message. ''' + self.setTagData('body',val)
- def setXHTML(self,val,xmllang=None): - """ Sets the xhtml text of the message (XEP-0071). - The parameter is the "inner html" to the body.""" - try: - if xmllang: - dom = NodeBuilder('<body xmlns="'+NS_XHTML+'" xml:lang="'+xmllang+'" >' + val + '</body>').getDom() - else: - dom = NodeBuilder('<body xmlns="'+NS_XHTML+'">'+val+'</body>',0).getDom() - if self.getTag('html'): - self.getTag('html').addChild(node=dom) - else: - self.setTag('html',namespace=NS_XHTML_IM).addChild(node=dom) - except Exception, e: - print "Error", e - #FIXME: log. we could not set xhtml (parse error, whatever) - def setSubject(self,val): - """ Sets the subject of the message. """ - self.setTagData('subject',val) - def setThread(self,val): - """ Sets the thread of the message. """ - self.setTagData('thread',val) - def buildReply(self,text=None): - """ Builds and returns another message object with specified text. - The to, from and thread properties of new message are pre-set as reply to this message. """ - m=Message(to=self.getFrom(),frm=self.getTo(),body=text,node=self) - th=self.getThread() - if th: m.setThread(th) - return m - def getStatusCode(self): - """Returns the status code of the message (for groupchat config - change)""" - attrs = [] - for xtag in self.getTags('x'): - for child in xtag.getTags('status'): - attrs.append(child.getAttr('code')) - return attrs + def setXHTML(self,val,xmllang=None): + ''' Sets the xhtml text of the message (XEP-0071). + The parameter is the "inner html" to the body.''' + try: + if xmllang: + dom = NodeBuilder('<body xmlns="'+NS_XHTML+'" xml:lang="'+xmllang+'" >' + val + '</body>').getDom() + else: + dom = NodeBuilder('<body xmlns="'+NS_XHTML+'">'+val+'</body>',0).getDom() + if self.getTag('html'): + self.getTag('html').addChild(node=dom) + else: + self.setTag('html',namespace=NS_XHTML_IM).addChild(node=dom) + except Exception, e: + print "Error", e + #FIXME: log. we could not set xhtml (parse error, whatever) + def setSubject(self,val): + ''' Sets the subject of the message. ''' + self.setTagData('subject',val) + def setThread(self,val): + ''' Sets the thread of the message. ''' + self.setTagData('thread',val) + def buildReply(self,text=None): + ''' Builds and returns another message object with specified text. + The to, from and thread properties of new message are pre-set as reply to this message. ''' + m=Message(to=self.getFrom(),frm=self.getTo(),body=text,node=self) + th=self.getThread() + if th: m.setThread(th) + return m + def getStatusCode(self): + '''Returns the status code of the message (for groupchat config + change)''' + attrs = [] + for xtag in self.getTags('x'): + for child in xtag.getTags('status'): + attrs.append(child.getAttr('code')) + return attrs
class Presence(Protocol): - """ XMPP Presence object.""" - def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None): - """ Create presence object. You can specify recipient, type of message, priority, show and status values - any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go. - Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. """ - Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) - if priority: self.setPriority(priority) - if show: self.setShow(show) - if status: self.setStatus(status) - def getPriority(self): - """ Returns the priority of the message. """ - return self.getTagData('priority') - def getShow(self): - """ Returns the show value of the message. """ - return self.getTagData('show') - def getStatus(self): - """ Returns the status string of the message. """ - return self.getTagData('status') - def setPriority(self,val): - """ Sets the priority of the message. """ - self.setTagData('priority',val) - def setShow(self,val): - """ Sets the show value of the message. """ - self.setTagData('show',val) - def setStatus(self,val): - """ Sets the status string of the message. """ - self.setTagData('status',val) + ''' XMPP Presence object.''' + def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None): + ''' Create presence object. You can specify recipient, type of message, priority, show and status values + any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go. + Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. ''' + Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) + if priority: self.setPriority(priority) + if show: self.setShow(show) + if status: self.setStatus(status) + def getPriority(self): + ''' Returns the priority of the message. ''' + return self.getTagData('priority') + def getShow(self): + ''' Returns the show value of the message. ''' + return self.getTagData('show') + def getStatus(self): + ''' Returns the status string of the message. ''' + return self.getTagData('status') + def setPriority(self,val): + ''' Sets the priority of the message. ''' + self.setTagData('priority',val) + def setShow(self,val): + ''' Sets the show value of the message. ''' + self.setTagData('show',val) + def setStatus(self,val): + ''' Sets the status string of the message. ''' + self.setTagData('status',val)
- def _muc_getItemAttr(self,tag,attr): - for xtag in self.getTags('x'): - if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): - continue - for child in xtag.getTags(tag): - return child.getAttr(attr) - def _muc_getSubTagDataAttr(self,tag,attr): - for xtag in self.getTags('x'): - if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): - continue - for child in xtag.getTags('item'): - for cchild in child.getTags(tag): - return cchild.getData(),cchild.getAttr(attr) - return None,None - def getRole(self): - """Returns the presence role (for groupchat)""" - return self._muc_getItemAttr('item','role') - def getAffiliation(self): - """Returns the presence affiliation (for groupchat)""" - return self._muc_getItemAttr('item','affiliation') - def getNewNick(self): - """Returns the status code of the presence (for groupchat)""" - return self._muc_getItemAttr('item','nick') - def getJid(self): - """Returns the presence jid (for groupchat)""" - return self._muc_getItemAttr('item','jid') - def getReason(self): - """Returns the reason of the presence (for groupchat)""" - return self._muc_getSubTagDataAttr('reason','')[0] - def getActor(self): - """Returns the reason of the presence (for groupchat)""" - return self._muc_getSubTagDataAttr('actor','jid')[1] - def getStatusCode(self): - """Returns the status code of the presence (for groupchat)""" - attrs = [] - for xtag in self.getTags('x'): - for child in xtag.getTags('status'): - attrs.append(child.getAttr('code')) - return attrs + def _muc_getItemAttr(self,tag,attr): + for xtag in self.getTags('x'): + if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): + continue + for child in xtag.getTags(tag): + return child.getAttr(attr) + def _muc_getSubTagDataAttr(self,tag,attr): + for xtag in self.getTags('x'): + if xtag.getNamespace() not in (NS_MUC_USER, NS_MUC_ADMIN): + continue + for child in xtag.getTags('item'): + for cchild in child.getTags(tag): + return cchild.getData(),cchild.getAttr(attr) + return None,None + def getRole(self): + '''Returns the presence role (for groupchat)''' + return self._muc_getItemAttr('item','role') + def getAffiliation(self): + '''Returns the presence affiliation (for groupchat)''' + return self._muc_getItemAttr('item','affiliation') + def getNewNick(self): + '''Returns the status code of the presence (for groupchat)''' + return self._muc_getItemAttr('item','nick') + def getJid(self): + '''Returns the presence jid (for groupchat)''' + return self._muc_getItemAttr('item','jid') + def getReason(self): + '''Returns the reason of the presence (for groupchat)''' + return self._muc_getSubTagDataAttr('reason','')[0] + def getActor(self): + '''Returns the reason of the presence (for groupchat)''' + return self._muc_getSubTagDataAttr('actor','jid')[1] + def getStatusCode(self): + '''Returns the status code of the presence (for groupchat)''' + attrs = [] + for xtag in self.getTags('x'): + for child in xtag.getTags('status'): + attrs.append(child.getAttr('code')) + return attrs
class Iq(Protocol): - """ XMPP Iq object - get/set dialog mechanism. """ - def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None): - """ Create Iq object. You can specify type, query namespace - any additional attributes, recipient of the iq, sender of the iq, any additional payload (f.e. jabber:x:data node) and namespace in one go. - Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. """ - Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node) - if payload: self.setQueryPayload(payload) - if queryNS: self.setQueryNS(queryNS) - def getQueryNS(self): - """ Return the namespace of the 'query' child element.""" - tag=self.getTag('query') - if tag: return tag.getNamespace() - def getQuerynode(self): - """ Return the 'node' attribute value of the 'query' child element.""" - return self.getTagAttr('query','node') - def getQueryPayload(self): - """ Return the 'query' child element payload.""" - tag=self.getTag('query') - if tag: return tag.getPayload() - def getQueryChildren(self): - """ Return the 'query' child element child nodes.""" - tag=self.getTag('query') - if tag: return tag.getChildren() - def setQueryNS(self,namespace): - """ Set the namespace of the 'query' child element.""" - self.setTag('query').setNamespace(namespace) - def setQueryPayload(self,payload): - """ Set the 'query' child element payload.""" - self.setTag('query').setPayload(payload) - def setQuerynode(self,node): - """ Set the 'node' attribute value of the 'query' child element.""" - self.setTagAttr('query','node',node) - def buildReply(self,typ): - """ Builds and returns another Iq object of specified type. - The to, from and query child node of new Iq are pre-set as reply to this Iq. """ - iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()}) - if self.getTag('query'): iq.setQueryNS(self.getQueryNS()) - return iq + ''' XMPP Iq object - get/set dialog mechanism. ''' + def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None): + ''' Create Iq object. You can specify type, query namespace + any additional attributes, recipient of the iq, sender of the iq, any additional payload (f.e. jabber:x:data node) and namespace in one go. + Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. ''' + Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node) + if payload: self.setQueryPayload(payload) + if queryNS: self.setQueryNS(queryNS) + def getQueryNS(self): + ''' Return the namespace of the 'query' child element.''' + tag=self.getTag('query') + if tag: return tag.getNamespace() + def getQuerynode(self): + ''' Return the 'node' attribute value of the 'query' child element.''' + return self.getTagAttr('query','node') + def getQueryPayload(self): + ''' Return the 'query' child element payload.''' + tag=self.getTag('query') + if tag: return tag.getPayload() + def getQueryChildren(self): + ''' Return the 'query' child element child nodes.''' + tag=self.getTag('query') + if tag: return tag.getChildren() + def setQueryNS(self,namespace): + ''' Set the namespace of the 'query' child element.''' + self.setTag('query').setNamespace(namespace) + def setQueryPayload(self,payload): + ''' Set the 'query' child element payload.''' + self.setTag('query').setPayload(payload) + def setQuerynode(self,node): + ''' Set the 'node' attribute value of the 'query' child element.''' + self.setTagAttr('query','node',node) + def buildReply(self,typ): + ''' Builds and returns another Iq object of specified type. + The to, from and query child node of new Iq are pre-set as reply to this Iq. ''' + iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()}) + if self.getTag('query'): iq.setQueryNS(self.getQueryNS()) + return iq
class ErrorNode(Node): - """ XMPP-style error element. - In the case of stanza error should be attached to XMPP stanza. - In the case of stream-level errors should be used separately. """ - def __init__(self,name,code=None,typ=None,text=None): - """ Create new error node object. - Mandatory parameter: name - name of error condition. - Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol.""" - if name in ERRORS: - cod,type_,txt=ERRORS[name] - ns=name.split()[0] - else: cod,ns,type_,txt='500',NS_STANZAS,'cancel','' - if typ: type_=typ - if code: cod=code - if text: txt=text - Node.__init__(self,'error',{},[Node(name)]) - if type_: self.setAttr('type',type_) - if not cod: self.setName('stream:error') - if txt: self.addChild(node=Node(ns+' text',{},[txt])) - if cod: self.setAttr('code',cod) + ''' XMPP-style error element. + In the case of stanza error should be attached to XMPP stanza. + In the case of stream-level errors should be used separately. ''' + def __init__(self,name,code=None,typ=None,text=None): + ''' Create new error node object. + Mandatory parameter: name - name of error condition. + Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol.''' + if name in ERRORS: + cod,type_,txt=ERRORS[name] + ns=name.split()[0] + else: cod,ns,type_,txt='500',NS_STANZAS,'cancel','' + if typ: type_=typ + if code: cod=code + if text: txt=text + Node.__init__(self,'error',{},[Node(name)]) + if type_: self.setAttr('type',type_) + if not cod: self.setName('stream:error') + if txt: self.addChild(node=Node(ns+' text',{},[txt])) + if cod: self.setAttr('code',cod)
class Error(Protocol): - """ Used to quickly transform received stanza into error reply.""" - def __init__(self,node,error,reply=1): - """ Create error reply basing on the received 'node' stanza and the 'error' error condition. - If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping) - specify the 'reply' argument as false.""" - if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node) - else: Protocol.__init__(self,node=node) - self.setError(error) - if node.getType()=='error': self.__str__=self.__dupstr__ - def __dupstr__(self,dup1=None,dup2=None): - """ Dummy function used as preventor of creating error node in reply to error node. - I.e. you will not be able to serialise "double" error into string. - """ - return '' + ''' Used to quickly transform received stanza into error reply.''' + def __init__(self,node,error,reply=1): + ''' Create error reply basing on the received 'node' stanza and the 'error' error condition. + If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping) + specify the 'reply' argument as false.''' + if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node) + else: Protocol.__init__(self,node=node) + self.setError(error) + if node.getType()=='error': self.__str__=self.__dupstr__ + def __dupstr__(self,dup1=None,dup2=None): + ''' Dummy function used as preventor of creating error node in reply to error node. + I.e. you will not be able to serialise "double" error into string. + ''' + return ''
class DataField(Node): - """ This class is used in the DataForm class to describe the single data item. - If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) - then you will need to work with instances of this class. """ - def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None): - """ Create new data field of specified name,value and type. - Also 'required','desc' and 'options' fields can be set. - Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled. - """ - Node.__init__(self,'field',node=node) - if name: self.setVar(name) - if isinstance(value, (list, tuple)): self.setValues(value) - elif value: self.setValue(value) - if typ: self.setType(typ) - elif not typ and not node: self.setType('text-single') - if required: self.setRequired(required) - if desc: self.setDesc(desc) - if options: self.setOptions(options) - def setRequired(self,req=1): - """ Change the state of the 'required' flag. """ - if req: self.setTag('required') - else: - try: self.delChild('required') - except ValueError: return - def isRequired(self): - """ Returns in this field a required one. """ - return self.getTag('required') - def setDesc(self,desc): - """ Set the description of this field. """ - self.setTagData('desc',desc) - def getDesc(self): - """ Return the description of this field. """ - return self.getTagData('desc') - def setValue(self,val): - """ Set the value of this field. """ - self.setTagData('value',val) - def getValue(self): - return self.getTagData('value') - def setValues(self,lst): - """ Set the values of this field as values-list. - Replaces all previous filed values! If you need to just add a value - use addValue method.""" - while self.getTag('value'): self.delChild('value') - for val in lst: self.addValue(val) - def addValue(self,val): - """ Add one more value to this field. Used in 'get' iq's or such.""" - self.addChild('value',{},[val]) - def getValues(self): - """ Return the list of values associated with this field.""" - ret=[] - for tag in self.getTags('value'): ret.append(tag.getData()) - return ret - def getOptions(self): - """ Return label-option pairs list associated with this field.""" - ret=[] - for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')]) - return ret - def setOptions(self,lst): - """ Set label-option pairs list associated with this field.""" - while self.getTag('option'): self.delChild('option') - for opt in lst: self.addOption(opt) - def addOption(self,opt): - """ Add one more label-option pair to this field.""" - if isinstance(opt, basestring): self.addChild('option').setTagData('value',opt) - else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1]) - def getType(self): - """ Get type of this field. """ - return self.getAttr('type') - def setType(self,val): - """ Set type of this field. """ - return self.setAttr('type',val) - def getVar(self): - """ Get 'var' attribute value of this field. """ - return self.getAttr('var') - def setVar(self,val): - """ Set 'var' attribute value of this field. """ - return self.setAttr('var',val) + ''' This class is used in the DataForm class to describe the single data item. + If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) + then you will need to work with instances of this class. ''' + def __init__(self,name=None,value=None,typ=None,required=0,desc=None,options=[],node=None): + ''' Create new data field of specified name,value and type. + Also 'required','desc' and 'options' fields can be set. + Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled. + ''' + Node.__init__(self,'field',node=node) + if name: self.setVar(name) + if isinstance(value, (list, tuple)): self.setValues(value) + elif value: self.setValue(value) + if typ: self.setType(typ) + elif not typ and not node: self.setType('text-single') + if required: self.setRequired(required) + if desc: self.setDesc(desc) + if options: self.setOptions(options) + def setRequired(self,req=1): + ''' Change the state of the 'required' flag. ''' + if req: self.setTag('required') + else: + try: self.delChild('required') + except ValueError: return + def isRequired(self): + ''' Returns in this field a required one. ''' + return self.getTag('required') + def setDesc(self,desc): + ''' Set the description of this field. ''' + self.setTagData('desc',desc) + def getDesc(self): + ''' Return the description of this field. ''' + return self.getTagData('desc') + def setValue(self,val): + ''' Set the value of this field. ''' + self.setTagData('value',val) + def getValue(self): + return self.getTagData('value') + def setValues(self,lst): + ''' Set the values of this field as values-list. + Replaces all previous filed values! If you need to just add a value - use addValue method.''' + while self.getTag('value'): self.delChild('value') + for val in lst: self.addValue(val) + def addValue(self,val): + ''' Add one more value to this field. Used in 'get' iq's or such.''' + self.addChild('value',{},[val]) + def getValues(self): + ''' Return the list of values associated with this field.''' + ret=[] + for tag in self.getTags('value'): ret.append(tag.getData()) + return ret + def getOptions(self): + ''' Return label-option pairs list associated with this field.''' + ret=[] + for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')]) + return ret + def setOptions(self,lst): + ''' Set label-option pairs list associated with this field.''' + while self.getTag('option'): self.delChild('option') + for opt in lst: self.addOption(opt) + def addOption(self,opt): + ''' Add one more label-option pair to this field.''' + if isinstance(opt, basestring): self.addChild('option').setTagData('value',opt) + else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1]) + def getType(self): + ''' Get type of this field. ''' + return self.getAttr('type') + def setType(self,val): + ''' Set type of this field. ''' + return self.setAttr('type',val) + def getVar(self): + ''' Get 'var' attribute value of this field. ''' + return self.getAttr('var') + def setVar(self,val): + ''' Set 'var' attribute value of this field. ''' + return self.setAttr('var',val)
class DataForm(Node): - """ DataForm class. Used for manipulating dataforms in XMPP. - Relevant XEPs: 0004, 0068, 0122. - Can be used in disco, pub-sub and many other applications.""" - def __init__(self, typ=None, data=[], title=None, node=None): - """ - Create new dataform of type 'typ'. 'data' is the list of DataField - instances that this dataform contains, 'title' - the title string. - You can specify the 'node' argument as the other node to be used as - base for constructing this dataform. + ''' DataForm class. Used for manipulating dataforms in XMPP. + Relevant XEPs: 0004, 0068, 0122. + Can be used in disco, pub-sub and many other applications.''' + def __init__(self, typ=None, data=[], title=None, node=None): + ''' + Create new dataform of type 'typ'. 'data' is the list of DataField + instances that this dataform contains, 'title' - the title string. + You can specify the 'node' argument as the other node to be used as + base for constructing this dataform.
- title and instructions is optional and SHOULD NOT contain newlines. - Several instructions MAY be present. - 'typ' can be one of ('form' | 'submit' | 'cancel' | 'result' ) - 'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively. - 'cancel' form can not contain any fields. All other forms contains AT LEAST one field. - 'title' MAY be included in forms of type "form" and "result" - """ - Node.__init__(self,'x',node=node) - if node: - newkids=[] - for n in self.getChildren(): - if n.getName()=='field': newkids.append(DataField(node=n)) - else: newkids.append(n) - self.kids=newkids - if typ: self.setType(typ) - self.setNamespace(NS_DATA) - if title: self.setTitle(title) - if isinstance(data, dict): - newdata=[] - for name in data.keys(): newdata.append(DataField(name,data[name])) - data=newdata - for child in data: - if isinstance(child, basestring): self.addInstructions(child) - elif child.__class__.__name__=='DataField': self.kids.append(child) - else: self.kids.append(DataField(node=child)) - def getType(self): - """ Return the type of dataform. """ - return self.getAttr('type') - def setType(self,typ): - """ Set the type of dataform. """ - self.setAttr('type',typ) - def getTitle(self): - """ Return the title of dataform. """ - return self.getTagData('title') - def setTitle(self,text): - """ Set the title of dataform. """ - self.setTagData('title',text) - def getInstructions(self): - """ Return the instructions of dataform. """ - return self.getTagData('instructions') - def setInstructions(self,text): - """ Set the instructions of dataform. """ - self.setTagData('instructions',text) - def addInstructions(self,text): - """ Add one more instruction to the dataform. """ - self.addChild('instructions',{},[text]) - def getField(self,name): - """ Return the datafield object with name 'name' (if exists). """ - return self.getTag('field',attrs={'var':name}) - def setField(self,name): - """ Create if nessessary or get the existing datafield object with name 'name' and return it. """ - f=self.getField(name) - if f: return f - return self.addChild(node=DataField(name)) - def asDict(self): - """ Represent dataform as simple dictionary mapping of datafield names to their values.""" - ret={} - for field in self.getTags('field'): - name=field.getAttr('var') - typ=field.getType() - if isinstance(typ, basestring) and typ.endswith('-multi'): - val=[] - for i in field.getTags('value'): val.append(i.getData()) - else: val=field.getTagData('value') - ret[name]=val - if self.getTag('instructions'): ret['instructions']=self.getInstructions() - return ret - def __getitem__(self,name): - """ Simple dictionary interface for getting datafields values by their names.""" - item=self.getField(name) - if item: return item.getValue() - raise IndexError('No such field') - def __setitem__(self,name,val): - """ Simple dictionary interface for setting datafields values by their names.""" - return self.setField(name).setValue(val) + title and instructions is optional and SHOULD NOT contain newlines. + Several instructions MAY be present. + 'typ' can be one of ('form' | 'submit' | 'cancel' | 'result' ) + 'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively. + 'cancel' form can not contain any fields. All other forms contains AT LEAST one field. + 'title' MAY be included in forms of type "form" and "result" + ''' + Node.__init__(self,'x',node=node) + if node: + newkids=[] + for n in self.getChildren(): + if n.getName()=='field': newkids.append(DataField(node=n)) + else: newkids.append(n) + self.kids=newkids + if typ: self.setType(typ) + self.setNamespace(NS_DATA) + if title: self.setTitle(title) + if isinstance(data, dict): + newdata=[] + for name in data.keys(): newdata.append(DataField(name,data[name])) + data=newdata + for child in data: + if isinstance(child, basestring): self.addInstructions(child) + elif child.__class__.__name__=='DataField': self.kids.append(child) + else: self.kids.append(DataField(node=child)) + def getType(self): + ''' Return the type of dataform. ''' + return self.getAttr('type') + def setType(self,typ): + ''' Set the type of dataform. ''' + self.setAttr('type',typ) + def getTitle(self): + ''' Return the title of dataform. ''' + return self.getTagData('title') + def setTitle(self,text): + ''' Set the title of dataform. ''' + self.setTagData('title',text) + def getInstructions(self): + ''' Return the instructions of dataform. ''' + return self.getTagData('instructions') + def setInstructions(self,text): + ''' Set the instructions of dataform. ''' + self.setTagData('instructions',text) + def addInstructions(self,text): + ''' Add one more instruction to the dataform. ''' + self.addChild('instructions',{},[text]) + def getField(self,name): + ''' Return the datafield object with name 'name' (if exists). ''' + return self.getTag('field',attrs={'var':name}) + def setField(self,name): + ''' Create if nessessary or get the existing datafield object with name 'name' and return it. ''' + f=self.getField(name) + if f: return f + return self.addChild(node=DataField(name)) + def asDict(self): + ''' Represent dataform as simple dictionary mapping of datafield names to their values.''' + ret={} + for field in self.getTags('field'): + name=field.getAttr('var') + typ=field.getType() + if isinstance(typ, basestring) and typ.endswith('-multi'): + val=[] + for i in field.getTags('value'): val.append(i.getData()) + else: val=field.getTagData('value') + ret[name]=val + if self.getTag('instructions'): ret['instructions']=self.getInstructions() + return ret + def __getitem__(self,name): + ''' Simple dictionary interface for getting datafields values by their names.''' + item=self.getField(name) + if item: return item.getValue() + raise IndexError('No such field') + def __setitem__(self,name,val): + ''' Simple dictionary interface for setting datafields values by their names.''' + return self.setField(name).setValue(val)
# vim: se ts=3:
Copied: branches/osx_newbuildsys/src/common/xmpp/proxy_connectors.py (from rev 10894, trunk/src/common/xmpp/proxy_connectors.py) =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/proxy_connectors.py (rev 0) +++ branches/osx_newbuildsys/src/common/xmpp/proxy_connectors.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,222 @@ +## proxy_connectors.py +## based on transports_nb.py +## +## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov +## modified by Dimitur Kirov dkirov@gmail.com +## modified by 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 struct, socket, base64 + +''' +Module containing classes for proxy connecting. So far its HTTP CONNECT +and SOCKS5 proxy. +Authentication to NTLM (Microsoft implementation) proxies can be next. +''' + +import logging +log = logging.getLogger('gajim.c.x.proxy_connectors') + +class ProxyConnector: + ''' + Interface for proxy-connecting object - when tunnneling XMPP over proxies, + some connecting process usually has to be done before opening stream. + Proxy connectors are used right after TCP connection is estabilished. + ''' + def __init__(self, send_method, onreceive, old_on_receive, on_success, + on_failure, xmpp_server, proxy_creds=(None,None)): + + self.send = send_method + self.onreceive = onreceive + self.old_on_receive = old_on_receive + self.on_success = on_success + self.on_failure = on_failure + self.xmpp_server = xmpp_server + self.proxy_user, self.proxy_pass = proxy_creds + self.old_on_receive = old_on_receive + + self.start_connecting() + + def start_connecting(self): + raise NotImplementedException() + + def connecting_over(self): + self.onreceive(self.old_on_receive) + self.on_success() + +class HTTPCONNECTConnector(ProxyConnector): + def start_connecting(self): + ''' + Connects to proxy, supplies login and password to it + (if were specified while creating instance). Instructs proxy to make + connection to the target server. + ''' + log.info('Proxy server contacted, performing authentification') + connector = ['CONNECT %s:%s HTTP/1.1' % self.xmpp_server, + 'Proxy-Connection: Keep-Alive', + 'Pragma: no-cache', + 'Host: %s:%s' % self.xmpp_server, + 'User-Agent: Gajim'] + if self.proxy_user and self.proxy_pass: + credentials = '%s:%s' % (self.proxy_user, self.proxy_pass) + credentials = base64.encodestring(credentials).strip() + connector.append('Proxy-Authorization: Basic '+credentials) + connector.append('\r\n') + self.onreceive(self._on_headers_sent) + self.send('\r\n'.join(connector)) + + def _on_headers_sent(self, reply): + if reply is None: + return + self.reply = reply.replace('\r', '') + try: + proto, code, desc = reply.split('\n')[0].split(' ', 2) + except: + log.error("_on_headers_sent:", exc_info=True) + #traceback.print_exc() + self.on_failure('Invalid proxy reply') + return + if code <> '200': + log.error('Invalid proxy reply: %s %s %s' % (proto, code, desc)) + self.on_failure('Invalid proxy reply') + return + if len(reply) != 2: + pass + self.connecting_over() + + + +class SOCKS5Connector(ProxyConnector): + ''' + SOCKS5 proxy connection class. Allows to use SOCKS5 proxies with + (optionally) simple authentication (only USERNAME/PASSWORD auth). + ''' + def start_connecting(self): + log.info('Proxy server contacted, performing authentification') + if self.proxy_user and self.proxy_pass: + to_send = '\x05\x02\x00\x02' + else: + to_send = '\x05\x01\x00' + self.onreceive(self._on_greeting_sent) + self.send(to_send) + + def _on_greeting_sent(self, reply): + if reply is None: + return + if len(reply) != 2: + self.on_failure('Invalid proxy reply') + return + if reply[0] != '\x05': + log.info('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + if reply[1] == '\x00': + return self._on_proxy_auth('\x01\x00') + elif reply[1] == '\x02': + to_send = '\x01' + chr(len(self.proxy_user)) + self.proxy_user +\ + chr(len(self.proxy_pass)) + self.proxy_pass + self.onreceive(self._on_proxy_auth) + self.send(to_send) + else: + if reply[1] == '\xff': + log.error('Authentification to proxy impossible: no acceptable ' + 'auth method') + self.on_failure('Authentification to proxy impossible: no ' + 'acceptable authentification method') + return + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + + def _on_proxy_auth(self, reply): + if reply is None: + return + if len(reply) != 2: + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + if reply[0] != '\x01': + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + if reply[1] != '\x00': + log.error('Authentification to proxy failed') + self.on_failure('Authentification to proxy failed') + return + log.info('Authentification successfull. Jabber server contacted.') + # Request connection + req = "\x05\x01\x00" + # If the given destination address is an IP address, we'll + # use the IPv4 address request even if remote resolving was specified. + try: + self.ipaddr = socket.inet_aton(self.xmpp_server[0]) + req = req + "\x01" + self.ipaddr + except socket.error: + # Well it's not an IP number, so it's probably a DNS name. +# if self.__proxy[3]==True: + # Resolve remotely + self.ipaddr = None + req = req + "\x03" + chr(len(self.xmpp_server[0])) + self.xmpp_server[0] +# else: +# # Resolve locally +# self.ipaddr = socket.inet_aton(socket.gethostbyname(self.xmpp_server[0])) +# req = req + "\x01" + ipaddr + req = req + struct.pack(">H",self.xmpp_server[1]) + self.onreceive(self._on_req_sent) + self.send(req) + + def _on_req_sent(self, reply): + if reply is None: + return + if len(reply) < 10: + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + if reply[0] != '\x05': + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + if reply[1] != "\x00": + # Connection failed + if ord(reply[1])<9: + errors = ['general SOCKS server failure', + 'connection not allowed by ruleset', + 'Network unreachable', + 'Host unreachable', + 'Connection refused', + 'TTL expired', + 'Command not supported', + 'Address type not supported' + ] + txt = errors[ord(reply[1])-1] + else: + txt = 'Invalid proxy reply' + log.error(txt) + self.on_failure(txt) + return + # Get the bound address/port + elif reply[3] == "\x01": + begin, end = 3, 7 + elif reply[3] == "\x03": + begin, end = 4, 4 + reply[4] + else: + log.error('Invalid proxy reply') + self.on_failure('Invalid proxy reply') + return + self.connecting_over() + + + + + + + +
Deleted: branches/osx_newbuildsys/src/common/xmpp/roster.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/roster.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/roster.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,197 +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 jid in self._data: del self._data[jid] - return - self.DEBUG('Setting roster item %s...'%jid,'ok') - if jid not in self._data: 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 'resources' not in self._data[jid]: 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 jid.getStripped() not in self._data: self._data[jid.getStripped()]={'name':None,'ask':None,'subscription':'none','groups':['Not in roster'],'resources':{}} - if not isinstance(self._data[jid.getStripped()]['resources'], 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 jid.getResource() in item['resources']: 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 resource in self._data[jid]['resources']: 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 item in self._data: 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 - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/roster_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/roster_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/roster_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -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,173 @@ mass-renaming of contacts. '''
-from roster import Roster -from protocol import NS_ROSTER +from protocol import * +from client import PlugIn
-class NonBlockingRoster(Roster): +import logging +log = logging.getLogger('gajim.c.x.roster_nb') + + +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._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)) + log.info('Roster requested from server') + + 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 + query = stanza.getTag('query') + if query: + for item in query.getTags('item'): + jid=item.getAttr('jid') + if item.getAttr('subscription')=='remove': + if self._data.has_key(jid): del self._data[jid] + return + log.info('Setting roster item %s...' % jid) + 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: + log.info('Setting roster item %s for resource %s...'%(jid.getStripped(),jid.getResource())) + 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 + + 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. @@ -58,4 +221,4 @@ else: return self
-# vim: se ts=3: \ No newline at end of file +# vim: se ts=3:
Deleted: branches/osx_newbuildsys/src/common/xmpp/session.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/session.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/session.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,360 +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. - - -""" -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. -""" - -__version__="$Id" - -import random -import simplexml -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=repr(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 socket.error: - received = '' - - if len(received): # length of 0 means disconnect - self.DEBUG(repr(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 isinstance(chunk, unicode): 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 Exception: - # UNLOCK_QUEUE - self.set_socket_state(SOCKET_DEAD) - self.DEBUG("Socket error while sending data",'error') - return self.terminate_stream() - self.DEBUG(repr(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 'id' not in attrs or not attrs['id']: - return self.terminate_stream(STREAM_INVALID_XML) - self.ID=attrs['id'] - if 'version' not in attrs: 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 'to' not in attrs: text+=' from="%s"'%self._owner.servernames[0] - else: text+=' from="%s"'%attrs['to'] - if 'xml:lang' in attrs: 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 'version' in attrs 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 'to' not in attrs: 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 'version' in attrs: - # send features - features=Node('stream:features') - if NS_TLS in self.waiting_features: - features.T.starttls.setNamespace(NS_TLS) - 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 Exception("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 Exception("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 - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/simplexml.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/simplexml.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/simplexml.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -14,19 +14,21 @@
# $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $
-"""Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams. -I'm personally using it in many other separate projects. It is designed to be as standalone as possible.""" +'''Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams. +I'm personally using it in many other separate projects. It is designed to be as standalone as possible.'''
import xml.parsers.expat +import logging +log = logging.getLogger('gajim.c.x.simplexml')
def XMLescape(txt): - """Returns provided string with symbols & < > " replaced by their respective XML entities.""" + '''Returns provided string with symbols & < > " replaced by their respective XML entities.''' # replace also FORM FEED and ESC, because they are not valid XML chars return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """).replace(u'\x0C', "").replace(u'\x1B', "")
ENCODING='utf-8' def ustr(what): - """Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.""" + '''Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.''' if isinstance(what, unicode): return what try: r=what.__str__() except AttributeError: r=str(what) @@ -34,7 +36,7 @@ return r
class Node(object): - """ Node class describes syntax of separate XML Node. It have a constructor that permits node creation + ''' Node class describes syntax of separate XML Node. It have a constructor that permits node creation from set of "namespace name", attributes and payload of text strings and other nodes. It does not natively support building node from text string and uses NodeBuilder class for that purpose. After creation node can be mangled in many ways so it can be completely changed. @@ -47,16 +49,16 @@ info with the "original" node that is changing the one node may influence the other. Though it is rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after replication (and using replication only to move upwards on the classes tree). - """ + ''' FORCE_NODE_RECREATION=0 def __init__(self, tag=None, attrs={}, payload=[], parent=None, nsp=None, node_built=False, node=None): - """ Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it + ''' Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings and child nodes that this node carries within itself and "parent" argument that is another node that this one will be the child of. Also the __init__ can be provided with "node" argument that is either a text string containing exactly one node or another Node instance to begin with. If both "node" and other arguments is provided then the node initially created as replica of "node" - provided and then modified to be compliant with other arguments.""" + provided and then modified to be compliant with other arguments.''' if node: if self.FORCE_NODE_RECREATION and isinstance(node, Node): node=str(node) @@ -108,8 +110,8 @@ return ns
def __str__(self,fancy=0): - """ Method used to dump node into textual representation. - if "fancy" argument is set to True produces indented output for readability.""" + ''' Method used to dump node into textual representation. + if "fancy" argument is set to True produces indented output for readability.''' s = (fancy-1) * 2 * ' ' + "<" + self.name if self.namespace: if not self.parent or self.parent.namespace!=self.namespace: @@ -125,7 +127,10 @@ for a in self.kids: if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt]) elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip()) - s = s + a.__str__(fancy and fancy+1) + if isinstance(a, str) or isinstance(a, unicode): + s = s + a.__str__() + else: + s = s + a.__str__(fancy and fancy+1) cnt=cnt+1 if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip()) @@ -138,8 +143,8 @@ if fancy: s = s + "\n" return s def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None): - """ If "node" argument is provided, adds it as child node. Else creates new node from - the other arguments' values and adds it as well.""" + ''' If "node" argument is provided, adds it as child node. Else creates new node from + the other arguments' values and adds it as well.''' if node: newnode=node node.parent = self @@ -149,45 +154,45 @@ self.kids.append(newnode) return newnode def addData(self, data): - """ Adds some CDATA to node. """ + ''' Adds some CDATA to node. ''' self.data.append(ustr(data)) def clearData(self): - """ Removes all CDATA from the node. """ + ''' Removes all CDATA from the node. ''' self.data=[] def delAttr(self, key): - """ Deletes an attribute "key" """ + ''' Deletes an attribute "key" ''' del self.attrs[key] def delChild(self, node, attrs={}): - """ Deletes the "node" from the node's childs list, if "node" is an instance. - Else deletes the first node that have specified name and (optionally) attributes. """ + ''' Deletes the "node" from the node's childs list, if "node" is an instance. + Else deletes the first node that have specified name and (optionally) attributes. ''' if not isinstance(node, Node): node=self.getTag(node,attrs) self.kids.remove(node) return node def getAttrs(self): - """ Returns all node's attributes as dictionary. """ + ''' Returns all node's attributes as dictionary. ''' return self.attrs def getAttr(self, key): - """ Returns value of specified attribute. """ + ''' Returns value of specified attribute. ''' return self.attrs.get(key) def getChildren(self): - """ Returns all node's child nodes as list. """ + ''' Returns all node's child nodes as list. ''' return self.kids def getData(self): - """ Returns all node CDATA as string (concatenated). """ + ''' Returns all node CDATA as string (concatenated). ''' return ''.join(self.data) def getName(self): - """ Returns the name of node """ + ''' Returns the name of node ''' return self.name def getNamespace(self): - """ Returns the namespace of node """ + ''' Returns the namespace of node ''' return self.namespace def getParent(self): - """ Returns the parent of node (if present). """ + ''' Returns the parent of node (if present). ''' return self.parent def getPayload(self): - """ Return the payload of node i.e. list of child nodes and CDATA entries. + ''' Return the payload of node i.e. list of child nodes and CDATA entries. F.e. for "<node>text1<nodea/><nodeb/> text2</node>" will be returned list: - ['text1', <nodea instance>, <nodeb instance>, ' text2']. """ + ['text1', <nodea instance>, <nodeb instance>, ' text2']. ''' ret=[] for i in range(len(self.kids)+len(self.data)+1): try: @@ -197,24 +202,24 @@ except IndexError: pass return ret def getTag(self, name, attrs={}, namespace=None): - """ Filters all child nodes using specified arguments as filter. - Returns the first found or None if not found. """ + ''' Filters all child nodes using specified arguments as filter. + Returns the first found or None if not found. ''' return self.getTags(name, attrs, namespace, one=1) def getTagAttr(self,tag,attr): - """ Returns attribute value of the child with specified name (or None if no such attribute).""" + ''' Returns attribute value of the child with specified name (or None if no such attribute).''' try: return self.getTag(tag).attrs[attr] except: return None def getTagData(self,tag): - """ Returns cocatenated CDATA of the child with specified name.""" + ''' Returns cocatenated CDATA of the child with specified name.''' try: return self.getTag(tag).getData() except Exception: return None def getTags(self, name, attrs={}, namespace=None, one=0): - """ Filters all child nodes using specified arguments as filter. - Returns the list of nodes found. """ + ''' Filters all child nodes using specified arguments as filter. + Returns the list of nodes found. ''' nodes=[] for node in self.kids: if namespace and namespace!=node.getNamespace(): continue @@ -226,7 +231,7 @@ if not one: return nodes
def iterTags(self, name, attrs={}, namespace=None): - """ Iterate over all children using specified arguments as filter. """ + ''' Iterate over all children using specified arguments as filter. ''' for node in self.kids: if namespace is not None and namespace!=node.getNamespace(): continue if node.getName() == name: @@ -237,61 +242,61 @@ yield node
def setAttr(self, key, val): - """ Sets attribute "key" with the value "val". """ + ''' Sets attribute "key" with the value "val". ''' self.attrs[key]=val def setData(self, data): - """ Sets node's CDATA to provided string. Resets all previous CDATA!""" + ''' Sets node's CDATA to provided string. Resets all previous CDATA!''' self.data=[ustr(data)] def setName(self,val): - """ Changes the node name. """ + ''' Changes the node name. ''' self.name = val def setNamespace(self, namespace): - """ Changes the node namespace. """ + ''' Changes the node namespace. ''' self.namespace=namespace def setParent(self, node): - """ Sets node's parent to "node". WARNING: do not checks if the parent already present - and not removes the node from the list of childs of previous parent. """ + ''' Sets node's parent to "node". WARNING: do not checks if the parent already present + and not removes the node from the list of childs of previous parent. ''' self.parent = node def setPayload(self,payload,add=0): - """ Sets node payload according to the list specified. WARNING: completely replaces all node's - previous content. If you wish just to add child or CDATA - use addData or addChild methods. """ + ''' Sets node payload according to the list specified. WARNING: completely replaces all node's + previous content. If you wish just to add child or CDATA - use addData or addChild methods. ''' if isinstance(payload, basestring): payload=[payload] if add: self.kids+=payload else: self.kids=payload def setTag(self, name, attrs={}, namespace=None): - """ Same as getTag but if the node with specified namespace/attributes not found, creates such - node and returns it. """ + ''' Same as getTag but if the node with specified namespace/attributes not found, creates such + node and returns it. ''' node=self.getTags(name, attrs, namespace=namespace, one=1) if node: return node else: return self.addChild(name, attrs, namespace=namespace) def setTagAttr(self,tag,attr,val): - """ Creates new node (if not already present) with name "tag" - and sets it's attribute "attr" to value "val". """ + ''' Creates new node (if not already present) with name "tag" + and sets it's attribute "attr" to value "val". ''' try: self.getTag(tag).attrs[attr]=val except Exception: self.addChild(tag,attrs={attr:val}) def setTagData(self,tag,val,attrs={}): - """ Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs" - and sets it's CDATA to string "val". """ + ''' Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs" + and sets it's CDATA to string "val". ''' try: self.getTag(tag,attrs).setData(ustr(val)) except Exception: self.addChild(tag,attrs,payload=[ustr(val)]) def has_attr(self,key): - """ Checks if node have attribute "key".""" + ''' Checks if node have attribute "key".''' return key in self.attrs def __getitem__(self,item): - """ Returns node's attribute "item" value. """ + ''' Returns node's attribute "item" value. ''' return self.getAttr(item) def __setitem__(self,item,val): - """ Sets node's attribute "item" value. """ + ''' Sets node's attribute "item" value. ''' return self.setAttr(item,val) def __delitem__(self,item): - """ Deletes node's attribute "item". """ + ''' Deletes node's attribute "item". ''' return self.delAttr(item) def __getattr__(self,attr): - """ Reduce memory usage caused by T/NT classes - use memory only when needed. """ + ''' Reduce memory usage caused by T/NT classes - use memory only when needed. ''' if attr=='T': self.T=T(self) return self.T @@ -301,7 +306,7 @@ raise AttributeError
class T: - """ Auxiliary class used to quick access to node's child nodes. """ + ''' Auxiliary class used to quick access to node's child nodes. ''' def __init__(self,node): self.__dict__['node']=node def __getattr__(self,attr): return self.node.setTag(attr) def __setattr__(self,attr,val): @@ -310,28 +315,26 @@ def __delattr__(self,attr): return self.node.delChild(attr)
class NT(T): - """ Auxiliary class used to quick create node's child nodes. """ + ''' Auxiliary class used to quick create node's child nodes. ''' def __getattr__(self,attr): return self.node.addChild(attr) def __setattr__(self,attr,val): if isinstance(val,Node): self.node.addChild(attr,node=val) else: return self.node.addChild(attr,payload=[val])
-DBG_NODEBUILDER = 'nodebuilder' class NodeBuilder: - """ Builds a Node class minidom from data parsed to it. This class used for two purposes: + ''' Builds a Node class minidom from data parsed to it. This class used for two purposes: 1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method. 2. Handling an incoming XML stream. This is done by mangling the __dispatch_depth parameter and redefining the dispatch method. - You do not need to use this class directly if you do not designing your own XML handler.""" + You do not need to use this class directly if you do not designing your own XML handler.''' def __init__(self,data=None,initial_node=None): - """ Takes two optional parameters: "data" and "initial_node". + ''' Takes two optional parameters: "data" and "initial_node". By default class initialised with empty Node class instance. Though, if "initial_node" is provided it used as "starting point". You can think about it as of "node upgrade". "data" (if provided) feeded to parser immidiatedly after instance init. - """ - self.DEBUG(DBG_NODEBUILDER, "Preparing to handle incoming XML stream.", 'start') - + ''' + log.debug("Preparing to handle incoming XML stream.") self._parser = xml.parsers.expat.ParserCreate() self._parser.StartElementHandler = self.starttag self._parser.EndElementHandler = self.endtag @@ -360,7 +363,7 @@ self.data_buffer = None
def destroy(self): - """ Method used to allow class instance to be garbage-collected. """ + ''' Method used to allow class instance to be garbage-collected. ''' self.check_data_buffer() self._parser.StartElementHandler = None self._parser.EndElementHandler = None @@ -368,10 +371,10 @@ self._parser.StartNamespaceDeclHandler = None
def starttag(self, tag, attrs): - """XML Parser callback. Used internally""" + '''XML Parser callback. Used internally''' self.check_data_buffer() self._inc_depth() - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, repr(attrs)), 'down') + log.info("STARTTAG.. DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`)) if self.__depth == self._dispatch_depth: if not self._mini_dom : self._mini_dom = Node(tag=tag, attrs=attrs, nsp = self._document_nsp, node_built=True) @@ -402,15 +405,15 @@ self._ptr.parent.data.append('') self.last_is_data = 0 def endtag(self, tag ): - """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s" % (self.__depth, tag), 'up') + '''XML Parser callback. Used internally''' + log.info("DEPTH -> %i , tag -> %s" % (self.__depth, tag)) self.check_data_buffer() if self.__depth == self._dispatch_depth: self.dispatch(self._mini_dom) elif self.__depth > self._dispatch_depth: self._ptr = self._ptr.parent else: - self.DEBUG(DBG_NODEBUILDER, "Got higher than dispatch level. Stream terminated?", 'stop') + log.info("Got higher than dispatch level. Stream terminated?") self._dec_depth() self.last_is_data = 0 if self.__depth == 0: self.stream_footer_received() @@ -424,26 +427,25 @@ self.last_is_data = 1
def handle_namespace_start(self, prefix, uri): - """XML Parser callback. Used internally""" + '''XML Parser callback. Used internally''' self.check_data_buffer() - def DEBUG(self, level, text, comment=None): - """ Gets all NodeBuilder walking events. Can be used for debugging if redefined.""" + def getDom(self): - """ Returns just built Node. """ + ''' Returns just built Node. ''' self.check_data_buffer() return self._mini_dom def dispatch(self,stanza): - """ Gets called when the NodeBuilder reaches some level of depth on it's way up with the built - node as argument. Can be redefined to convert incoming XML stanzas to program events. """ + ''' Gets called when the NodeBuilder reaches some level of depth on it's way up with the built + node as argument. Can be redefined to convert incoming XML stanzas to program events. ''' def stream_header_received(self,ns,tag,attrs): - """ Method called when stream just opened. """ + ''' Method called when stream just opened. ''' self.check_data_buffer() def stream_footer_received(self): - """ Method called when stream just closed. """ + ''' Method called when stream just closed. ''' self.check_data_buffer()
def has_received_endtag(self, level=0): - """ Return True if at least one end tag was seen (at level) """ + ''' Return True if at least one end tag was seen (at level) ''' return self.__depth <= level and self.__max_depth > level
def _inc_depth(self): @@ -456,14 +458,14 @@ self.__depth -= 1
def XML2Node(xml): - """ Converts supplied textual string into XML node. Handy f.e. for reading configuration file. - Raises xml.parser.expat.parsererror if provided string is not well-formed XML. """ + ''' Converts supplied textual string into XML node. Handy f.e. for reading configuration file. + Raises xml.parser.expat.parsererror if provided string is not well-formed XML. ''' return NodeBuilder(xml).getDom()
def BadXML2Node(xml): - """ Converts supplied textual string into XML node. Survives if xml data is cutted half way round. + ''' Converts supplied textual string into XML node. Survives if xml data is cutted half way round. I.e. "<html>some text <br>some more text". Will raise xml.parser.expat.parsererror on misplaced - tags though. F.e. "<b>some text <br>some more text</b>" will not work.""" + tags though. F.e. "<b>some text <br>some more text</b>" will not work.''' return NodeBuilder(xml).getDom()
# vim: se ts=3:
Copied: branches/osx_newbuildsys/src/common/xmpp/tls_nb.py (from rev 10894, trunk/src/common/xmpp/tls_nb.py) =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/tls_nb.py (rev 0) +++ branches/osx_newbuildsys/src/common/xmpp/tls_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,375 @@ +## tls_nb.py +## based on transports_nb.py +## +## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov +## modified by Dimitur Kirov dkirov@gmail.com +## modified by 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 socket +from client import PlugIn +from protocol import * + +import sys +import os +import errno +import time + +import traceback + +import logging +log = logging.getLogger('gajim.c.x.tls_nb') + +USE_PYOPENSSL = False + +PYOPENSSL = 'PYOPENSSL' +PYSTDLIB = 'PYSTDLIB' + +try: + #raise ImportError("Manually disabled PyOpenSSL") + import OpenSSL.SSL + import OpenSSL.crypto + USE_PYOPENSSL = True + log.info("PyOpenSSL loaded") +except ImportError: + log.debug("Import of PyOpenSSL failed:", exc_info=True) + + # FIXME: Remove these prints before release, replace with a warning dialog. + print >> sys.stderr, "=" * 79 + print >> sys.stderr, "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)." + print >> sys.stderr, "=" * 79 + + +def torf(cond, tv, fv): + if cond: return tv + return fv + +def gattr(obj, attr, default=None): + try: + return getattr(obj, attr) + except: + return default + +class SSLWrapper: + class Error(IOError): + def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None): + self.parent = IOError + + errno = errno or gattr(exc, 'errno') + strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args') + if not isinstance(strerror, basestring): strerror = repr(strerror) + + self.sock = sock + self.exc = exc + self.peer = peer + self.exc_name = None + self.exc_args = None + self.exc_str = None + self.exc_repr = None + + if self.exc is not None: + self.exc_name = str(self.exc.__class__) + self.exc_args = gattr(self.exc, 'args') + self.exc_str = str(self.exc) + self.exc_repr = repr(self.exc) + if not errno: + try: + if isinstance(exc, OpenSSL.SSL.SysCallError): + if self.exc_args[0] > 0: + errno = self.exc_args[0] + strerror = self.exc_args[1] + except: pass + + self.parent.__init__(self, errno, strerror) + + if self.peer is None and sock is not None: + try: + ppeer = self.sock.getpeername() + if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \ + and isinstance(ppeer[1], int): + self.peer = ppeer + except: pass + + def __str__(self): + s = str(self.__class__) + if self.peer: s += " for %s:%d" % self.peer + if self.errno is not None: s += ": [Errno: %d]" % self.errno + if self.strerror: s += " (%s)" % self.strerror + if self.exc_name: + s += ", Caused by %s" % self.exc_name + if self.exc_str: + if self.strerror: s += "(%s)" % self.exc_str + else: s += "(%s)" % str(self.exc_args) + return s + + def __init__(self, sslobj, sock=None): + self.sslobj = sslobj + self.sock = sock + log.debug("%s.__init__ called with %s", self.__class__, sslobj) + + def recv(self, data, flags=None): + ''' Receive wrapper for SSL object + + We can return None out of this function to signal that no data is + available right now. Better than an exception, which differs + depending on which SSL lib we're using. Unfortunately returning '' + can indicate that the socket has been closed, so to be sure, we avoid + this by returning None. ''' + + raise NotImplementedException() + + def send(self, data, flags=None, now = False): + raise NotImplementedException() + +class PyOpenSSLWrapper(SSLWrapper): + '''Wrapper class for PyOpenSSL's recv() and send() methods''' + + def __init__(self, *args): + self.parent = SSLWrapper + self.parent.__init__(self, *args) + + def is_numtoolarge(self, e): + t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large') + return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \ + isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \ + e.args[0][0] == e.args[0][1] == t + + def recv(self, bufsize, flags=None): + retval = None + try: + if flags is None: retval = self.sslobj.recv(bufsize) + else: retval = self.sslobj.recv(bufsize, flags) + except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: + log.debug("Recv: Want-error: " + repr(e)) + except OpenSSL.SSL.SysCallError, e: + log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) + #traceback.print_exc() + raise SSLWrapper.Error(self.sock or self.sslobj, e) + except OpenSSL.SSL.Error, e: + if self.is_numtoolarge(e): + # warn, but ignore this exception + log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)") + else: + log.debug("Recv: Caught OpenSSL.SSL.Error:", exc_info=True) + #traceback.print_exc() + #print "Current Stack:" + #traceback.print_stack() + raise SSLWrapper.Error(self.sock or self.sslobj, e) + return retval + + def send(self, data, flags=None, now = False): + try: + if flags is None: return self.sslobj.send(data) + else: return self.sslobj.send(data, flags) + except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: + #log.debug("Send: " + repr(e)) + time.sleep(0.1) # prevent 100% CPU usage + except OpenSSL.SSL.SysCallError, e: + log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) + #traceback.print_exc() + raise SSLWrapper.Error(self.sock or self.sslobj, e) + except OpenSSL.SSL.Error, e: + if self.is_numtoolarge(e): + # warn, but ignore this exception + log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)") + else: + log.error("Send: Caught OpenSSL.SSL.Error:", exc_info=True) + #traceback.print_exc() + #print "Current Stack:" + #traceback.print_stack() + raise SSLWrapper.Error(self.sock or self.sslobj, e) + return 0 + +class StdlibSSLWrapper(SSLWrapper): + '''Wrapper class for Python socket.ssl read() and write() methods''' + + def __init__(self, *args): + self.parent = SSLWrapper + self.parent.__init__(self, *args) + + def recv(self, bufsize, flags=None): + # we simply ignore flags since ssl object doesn't support it + try: + return self.sslobj.read(bufsize) + except socket.sslerror, e: + log.debug("Recv: Caught socket.sslerror: " + repr(e), exc_info=True) + if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): + raise SSLWrapper.Error(self.sock or self.sslobj, e) + return None + + def send(self, data, flags=None, now = False): + # we simply ignore flags since ssl object doesn't support it + try: + return self.sslobj.write(data) + except socket.sslerror, e: + log.debug("Send: Caught socket.sslerror:", exc_info=True) + if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): + raise SSLWrapper.Error(self.sock or self.sslobj, e) + return 0 + + +class NonBlockingTLS(PlugIn): + ''' TLS connection used to encrypts already estabilished tcp connection.''' + + def __init__(self, cacerts, mycerts): + ''' + :param cacerts: path to pem file with certificates of known XMPP servers + :param mycerts: path to pem file with certificates of user trusted servers + ''' + PlugIn.__init__(self) + self.cacerts = cacerts + self.mycerts = mycerts + + # from ssl.h (partial extract) + ssl_h_bits = { "SSL_ST_CONNECT": 0x1000, "SSL_ST_ACCEPT": 0x2000, + "SSL_CB_LOOP": 0x01, "SSL_CB_EXIT": 0x02, + "SSL_CB_READ": 0x04, "SSL_CB_WRITE": 0x08, + "SSL_CB_ALERT": 0x4000, + "SSL_CB_HANDSHAKE_START": 0x10, "SSL_CB_HANDSHAKE_DONE": 0x20} + + def PlugIn(self, owner): + ''' + start using encryption immediately + ''' + log.info('Starting TLS estabilishing') + PlugIn.PlugIn(self, owner) + try: + res = self._startSSL() + except Exception, e: + log.error("PlugIn: while trying _startSSL():", exc_info=True) + return False + return res + + + + def _dumpX509(self, cert, stream=sys.stderr): + print >> stream, "Digest (SHA-1):", cert.digest("sha1") + print >> stream, "Digest (MD5):", cert.digest("md5") + print >> stream, "Serial #:", cert.get_serial_number() + print >> stream, "Version:", cert.get_version() + print >> stream, "Expired:", torf(cert.has_expired(), "Yes", "No") + print >> stream, "Subject:" + self._dumpX509Name(cert.get_subject(), stream) + print >> stream, "Issuer:" + self._dumpX509Name(cert.get_issuer(), stream) + self._dumpPKey(cert.get_pubkey(), stream) + + def _dumpX509Name(self, name, stream=sys.stderr): + print >> stream, "X509Name:", str(name) + + def _dumpPKey(self, pkey, stream=sys.stderr): + typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"} + print >> stream, "PKey bits:", pkey.bits() + print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type()) + + def _startSSL(self): + ''' Immediatedly switch socket to TLS mode. Used internally.''' + log.debug("_startSSL called") + + if USE_PYOPENSSL: result = self._startSSL_pyOpenSSL() + else: result = self._startSSL_stdlib() + + if result: + log.debug("Synchronous handshake completed") + return True + else: + return False + + + def _startSSL_pyOpenSSL(self): + log.debug("_startSSL_pyOpenSSL called") + tcpsock = self._owner + # FIXME: should method be configurable? + #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) + 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) + try: + tcpsock._sslContext.load_verify_locations(self.cacerts) + except: + log.warning('Unable to load SSL certificates from file %s' % \ + os.path.abspath(self.cacerts)) + # load users certs + if os.path.isfile(self.mycerts): + store = tcpsock._sslContext.get_cert_store() + f = open(self.mycerts) + lines = f.readlines() + i = 0 + begin = -1 + for line in lines: + if 'BEGIN CERTIFICATE' in line: + begin = i + elif 'END CERTIFICATE' in line and begin > -1: + cert = ''.join(lines[begin:i+2]) + try: + X509cert = OpenSSL.crypto.load_certificate( + OpenSSL.crypto.FILETYPE_PEM, cert) + store.add_cert(X509cert) + except OpenSSL.crypto.Error, exception_obj: + log.warning('Unable to load a certificate from file %s: %s' %\ + (self.mycerts, exception_obj.args[0][0][2])) + except: + log.warning('Unknown error while loading certificate from file %s' %\ + self.mycerts) + begin = -1 + i += 1 + tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) + tcpsock._sslObj.set_connect_state() # set to client mode + + wrapper = PyOpenSSLWrapper(tcpsock._sslObj) + tcpsock._recv = wrapper.recv + tcpsock._send = wrapper.send + + log.debug("Initiating handshake...") + tcpsock._sslObj.setblocking(True) + try: + tcpsock._sslObj.do_handshake() + except: + log.error('Error while TLS handshake: ', exc_info=True) + return False + tcpsock._sslObj.setblocking(False) + self._owner.ssl_lib = PYOPENSSL + return True + + + def _startSSL_stdlib(self): + log.debug("_startSSL_stdlib called") + tcpsock=self._owner + try: + tcpsock._sock.setblocking(True) + tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) + tcpsock._sock.setblocking(False) + tcpsock._sslIssuer = tcpsock._sslObj.issuer() + tcpsock._sslServer = tcpsock._sslObj.server() + wrapper = StdlibSSLWrapper(tcpsock._sslObj, tcpsock._sock) + tcpsock._recv = wrapper.recv + tcpsock._send = wrapper.send + except: + log.error("Exception caught in _startSSL_stdlib:", exc_info=True) + return False + self._owner.ssl_lib = PYSTDLIB + return True + + def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): + # Exceptions can't propagate up through this callback, so print them here. + try: + self._owner.ssl_fingerprint_sha1 = cert.digest('sha1') + if errnum == 0: + return True + self._owner.ssl_errnum = errnum + self._owner.ssl_cert_pem = OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_PEM, cert) + return True + except: + log.error("Exception caught in _ssl_info_callback:", exc_info=True) + traceback.print_exc() # Make sure something is printed, even if log is disabled. +
Deleted: branches/osx_newbuildsys/src/common/xmpp/transports.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/transports.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/transports.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1,292 +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 -import select -import base64 -import dispatcher -from simplexml import ustr -from client import PlugIn -from protocol import * -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 - -DBG_SOCKET = "socket" - -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 = DBG_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) - owner.debug_flags.append(DBG_SOCKET) - 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"%repr(server),'start') - return 'ok' - except Exception: - continue - except Exception: - 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 socket.error: - received = '' - - while temp_failure_retry(select.select,[self._sock],[],[],0)[0]: - try: - add = self._recv(1024000) - except socket.error: - 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 isinstance(raw_data, unicode): raw_data = raw_data.encode('utf-8') - elif not isinstance(raw_data, str): 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 Exception: - 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 'user' in self._proxy and 'password' in self._proxy: - 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 Exception('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 'TLS' in owner.__dict__: return # Already enabled. - PlugIn.PlugIn(self,owner) - self.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) - -# vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/xmpp/transports_nb.py =================================================================== --- branches/osx_newbuildsys/src/common/xmpp/transports_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/xmpp/transports_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -3,6 +3,7 @@ ## ## Copyright (C) 2003-2004 Alexey "Snake" Nezhdanov ## modified by Dimitur Kirov dkirov@gmail.com +## modified by 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 @@ -14,254 +15,230 @@ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details.
-import socket -import base64 -import dispatcher_nb -import struct from simplexml import ustr from client import PlugIn from idlequeue import IdleObject from protocol import * -from transports import * +import proxy_connectors +import tls_nb
+import socket import sys import os import errno import time - import traceback +import base64
import logging log = logging.getLogger('gajim.c.x.transports_nb')
-import common.gajim +def urisplit(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
-USE_PYOPENSSL = False +def get_proxy_data_from_dict(proxy): + tcp_host, tcp_port, proxy_user, proxy_pass = None, None, None, None + type = proxy['type'] + if type == 'bosh' and not proxy['bosh_useproxy']: + # with BOSH not over proxy we have to parse the hostname from BOSH URI + tcp_host, tcp_port = urisplit(proxy['bosh_uri'])[1], proxy['bosh_port'] + else: + # with proxy!=bosh or with bosh over HTTP proxy we're connecting to proxy + # machine + tcp_host, tcp_port = proxy['host'], proxy['port'] + if proxy['useauth']: + proxy_user, proxy_pass = proxy['user'], proxy['pass'] + return tcp_host, tcp_port, proxy_user, proxy_pass
-DBG_NONBLOCKINGTLS= 'NonBlockingTLS' -try: - #raise ImportError("Manually disabled PyOpenSSL") - import OpenSSL.SSL - import OpenSSL.crypto - USE_PYOPENSSL = True - log.info("PyOpenSSL loaded") -except ImportError: - log.debug("Import of PyOpenSSL failed:", exc_info=True) - - # FIXME: Remove these prints before release, replace with a warning dialog. - print >> sys.stderr, "=" * 79 - print >> sys.stderr, "PyOpenSSL not found, falling back to Python builtin SSL objects (insecure)." - print >> sys.stderr, "=" * 79 - # timeout to connect to the server socket, it doesn't include auth CONNECT_TIMEOUT_SECONDS = 30
# how long to wait for a disconnect to complete -DISCONNECT_TIMEOUT_SECONDS = 10 +DISCONNECT_TIMEOUT_SECONDS = 5
# size of the buffer which reads data from server # if lower, more stanzas will be fragmented and processed twice RECV_BUFSIZE = 32768 # 2x maximum size of ssl packet, should be plenty #RECV_BUFSIZE = 16 # FIXME: (#2634) gajim breaks with this setting: it's inefficient but should work.
-def torf(cond, tv, fv): - if cond: return tv - return fv +DATA_RECEIVED='DATA RECEIVED' +DATA_SENT='DATA SENT'
-def gattr(obj, attr, default=None): - try: - return getattr(obj, attr) - except Exception: - return default +DISCONNECTED = 'DISCONNECTED' +DISCONNECTING = 'DISCONNECTING' +CONNECTING = 'CONNECTING' +PROXY_CONNECTING = 'PROXY_CONNECTING' +CONNECTED = 'CONNECTED' +STATES = [DISCONNECTED, CONNECTING, PROXY_CONNECTING, CONNECTED, DISCONNECTING] +# Transports have different arguments in constructor and same connect() method.
-class SSLWrapper: - class Error(IOError): - def __init__(self, sock=None, exc=None, errno=None, strerror=None, peer=None): - self.parent = IOError +class NonBlockingTransport(PlugIn): + ''' + Abstract class representing a trasport - object responsible for connecting to + XMPP server and putting stanzas on wire in desired form. + ''' + def __init__(self, raise_event, on_disconnect, idlequeue, estabilish_tls, certs): + ''' + Each trasport class can have different constructor but it has to have at + least all the arguments of NonBlockingTransport constructor. + + :param raise_event: callback for monitoring of sent and received data + :param on_disconnect: callback called on disconnection during runtime + :param idlequeue: processing idlequeue + :param estabilish_tls: boolean whether to estabilish TLS connection after TCP + connection is done + :param certs: tuple of (cacerts, mycerts) see tls_nb.NonBlockingTLS + constructor for more details + ''' + PlugIn.__init__(self) + self.raise_event = raise_event + self.on_disconnect = on_disconnect + self.on_connect = None + self.on_connect_failure = None + self.idlequeue = idlequeue + self.on_receive = None + self.server = None + self.port = None + self.set_state(DISCONNECTED) + self.estabilish_tls = estabilish_tls + self.certs = certs + # type of used ssl lib (if any) will be assigned to this member var + self.ssl_lib = None + self._exported_methods=[self.onreceive, self.set_send_timeout, + self.set_timeout, self.remove_timeout, self.start_disconnect]
- errno = errno or gattr(exc, 'errno') - strerror = strerror or gattr(exc, 'strerror') or gattr(exc, 'args') - if not isinstance(strerror, basestring): strerror = repr(strerror) + # time to wait for SOME stanza to come and then send keepalive + self.sendtimeout = 0
- self.sock = sock - self.exc = exc - self.peer = peer - self.exc_name = None - self.exc_args = None - self.exc_str = None - self.exc_repr = None + # in case we want to something different than sending keepalives + self.on_timeout = None
- if self.exc is not None: - self.exc_name = str(self.exc.__class__) - self.exc_args = gattr(self.exc, 'args') - self.exc_str = str(self.exc) - self.exc_repr = repr(self.exc) - if not errno: - try: - if isinstance(exc, OpenSSL.SSL.SysCallError): - if self.exc_args[0] > 0: - errno = self.exc_args[0] - strerror = self.exc_args[1] - except Exception: - pass + def plugin(self, owner): + owner.Connection=self
- self.parent.__init__(self, errno, strerror) + def plugout(self): + self._owner.Connection = None + self._owner = None + self.disconnect(do_callback=False)
- if self.peer is None and sock is not None: - try: - ppeer = self.sock.getpeername() - if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \ - and isinstance(ppeer[1], int): - self.peer = ppeer - except Exception: - pass + def connect(self, conn_5tuple, on_connect, on_connect_failure): + ''' + Creates and connects transport to server and port defined in conn_5tupe which + should be item from list returned from getaddrinfo. + :param conn_5tuple: 5-tuple returned from getaddrinfo + :param on_connect: callback called on successful connect to the server + :param on_connect_failure: callback called on failure when connecting + ''' + self.on_connect = on_connect + self.on_connect_failure = on_connect_failure + self.server, self.port = conn_5tuple[4][:2] + self.conn_5tuple = conn_5tuple
- def __str__(self): - s = str(self.__class__) - if self.peer: s += " for %s:%d" % self.peer - if self.errno is not None: s += ": [Errno: %d]" % self.errno - if self.strerror: s += " (%s)" % self.strerror - if self.exc_name: - s += ", Caused by %s" % self.exc_name - if self.exc_str: - if self.strerror: s += "(%s)" % self.exc_str - else: s += "(%s)" % str(self.exc_args) - return s + def set_state(self, newstate): + assert(newstate in STATES) + self.state = newstate
- def __init__(self, sslobj, sock=None): - self.sslobj = sslobj - self.sock = sock - log.debug("%s.__init__ called with %s", self.__class__, sslobj) + def get_state(self): + return self.state
- def recv(self, data, flags=None): - """ Receive wrapper for SSL object + def _on_connect(self): + ''' preceeds call of on_connect callback ''' + # data is reference to socket wrapper instance. We don't need it in client + # because + self.set_state(CONNECTED) + self.on_connect()
- We can return None out of this function to signal that no data is - available right now. Better than an exception, which differs - depending on which SSL lib we're using. Unfortunately returning '' - can indicate that the socket has been closed, so to be sure, we avoid - this by returning None. """ + def _on_connect_failure(self,err_message): + ''' preceeds call of on_connect_failure callback ''' + # In case of error while connecting we need to disconnect transport + # but we don't want to call DisconnectHandlers from client, + # thus the do_callback=False + self.disconnect(do_callback=False) + self.on_connect_failure(err_message=err_message)
- raise NotImplementedError() + def send(self, raw_data, now=False): + if self.get_state() == DISCONNECTED: + log.error('Unable to send %s \n because state is %s.' % + (raw_data, self.get_state()))
- def send(self, data, flags=None, now = False): - raise NotImplementedError() + def disconnect(self, do_callback=True): + self.set_state(DISCONNECTED) + if do_callback: + # invoke callback given in __init__ + self.on_disconnect()
-class PyOpenSSLWrapper(SSLWrapper): - '''Wrapper class for PyOpenSSL's recv() and send() methods''' + def onreceive(self, recv_handler): + ''' + Sets the on_receive callback. Do not confuse it with on_receive() method, + which is the callback itself. + onreceive(None) sets callback to Dispatcher.ProcessNonBlocking which is the + default one that will decide what to do with received stanza based on its + tag name and namespace. + ''' + if not recv_handler: + if hasattr(self._owner, 'Dispatcher'): + self.on_receive = self._owner.Dispatcher.ProcessNonBlocking + else: + self.on_receive = None + return + self.on_receive = recv_handler
- def __init__(self, *args): - self.parent = SSLWrapper - self.parent.__init__(self, *args) + def tcp_connecting_started(self): + self.set_state(CONNECTING)
- def is_numtoolarge(self, e): - t = ('asn1 encoding routines', 'a2d_ASN1_OBJECT', 'first num too large') - return isinstance(e.args, (list, tuple)) and len(e.args) == 1 and \ - isinstance(e.args[0], (list, tuple)) and len(e.args[0]) == 2 and \ - e.args[0][0] == e.args[0][1] == t + def read_timeout(self): + ''' called when there's no response from server in defined timeout ''' + if self.on_timeout: + self.on_timeout() + self.renew_send_timeout()
- def recv(self, bufsize, flags=None): - retval = None - try: - if flags is None: retval = self.sslobj.recv(bufsize) - else: retval = self.sslobj.recv(bufsize, flags) - except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: - pass - # log.debug("Recv: " + repr(e)) - except OpenSSL.SSL.SysCallError, e: - log.debug("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) - #traceback.print_exc() - raise SSLWrapper.Error(self.sock or self.sslobj, e) - except OpenSSL.SSL.Error, e: - if self.is_numtoolarge(e): - # warn, but ignore this exception - log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)") - else: - log.debug("Recv: Caught OpenSSL.SSL.Error:", exc_info=True) - #traceback.print_exc() - #print "Current Stack:" - #traceback.print_stack() - raise SSLWrapper.Error(self.sock or self.sslobj, e) - return retval + def renew_send_timeout(self): + if self.on_timeout and self.sendtimeout > 0: + self.set_timeout(self.sendtimeout) + else: + self.remove_timeout()
- def send(self, data, flags=None, now = False): - try: - if flags is None: return self.sslobj.send(data) - else: return self.sslobj.send(data, flags) - except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: - #log.debug("Send: " + repr(e)) - time.sleep(0.1) # prevent 100% CPU usage - except OpenSSL.SSL.SysCallError, e: - log.error("Send: Got OpenSSL.SSL.SysCallError: " + repr(e), exc_info=True) - #traceback.print_exc() - raise SSLWrapper.Error(self.sock or self.sslobj, e) - except OpenSSL.SSL.Error, e: - if self.is_numtoolarge(e): - # warn, but ignore this exception - log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)") - else: - log.error("Send: Caught OpenSSL.SSL.Error:", exc_info=True) - #traceback.print_exc() - #print "Current Stack:" - #traceback.print_stack() - raise SSLWrapper.Error(self.sock or self.sslobj, e) - return 0 + def set_timeout(self, timeout): + self.idlequeue.set_read_timeout(self.fd, timeout)
-class StdlibSSLWrapper(SSLWrapper): - '''Wrapper class for Python's socket.ssl read() and write() methods''' + def get_fd(self): + pass
- def __init__(self, *args): - self.parent = SSLWrapper - self.parent.__init__(self, *args) + def remove_timeout(self): + self.idlequeue.remove_timeout(self.fd)
- def recv(self, bufsize, flags=None): - # we simply ignore flags since ssl object doesn't support it - try: - return self.sslobj.read(bufsize) - except socket.sslerror, e: - #log.debug("Recv: Caught socket.sslerror:", exc_info=True) - #traceback.print_exc() - if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): - raise SSLWrapper.Error(self.sock or self.sslobj, e) - return None + def set_send_timeout(self, timeout, on_timeout): + self.sendtimeout = timeout + if self.sendtimeout > 0: + self.on_timeout = on_timeout + else: + self.on_timeout = None
- def send(self, data, flags=None, now = False): - # we simply ignore flags since ssl object doesn't support it - try: - return self.sslobj.write(data) - except socket.sslerror, e: - #log.debug("Send: Caught socket.sslerror:", exc_info=True) - #traceback.print_exc() - if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): - raise SSLWrapper.Error(self.sock or self.sslobj, e) - return 0 + def start_disconnect(self): + self.set_state(DISCONNECTING)
-class NonBlockingTcp(PlugIn, IdleObject): - ''' This class can be used instead of transports.Tcp in threadless implementations ''' - def __init__(self, on_connect = None, on_connect_failure = None, server=None, use_srv = True): - ''' Cache connection point 'server'. 'server' is the tuple of (host, port) - absolutely the same as standard tcp socket uses. - on_connect - called when we connect to the socket - on_connect_failure - called if there was error connecting to socket - ''' - IdleObject.__init__(self) - PlugIn.__init__(self) - self.DBG_LINE='socket' - self._exported_methods=[self.send, self.disconnect, self.onreceive, self.set_send_timeout, - self.start_disconnect, self.set_timeout, self.remove_timeout] - self._server = server - self._hostfqdn = server[0] - self.on_connect = on_connect - self.on_connect_failure = on_connect_failure - self.on_receive = None - self.on_disconnect = None - self.printed_error = False
- # 0 - not connected - # 1 - connected - # -1 - about to disconnect (when we wait for final events to complete) - # -2 - disconnected - self.state = 0 +class NonBlockingTCP(NonBlockingTransport, IdleObject): + ''' + Non-blocking TCP socket wrapper. It is used for simple XMPP connection. Can be + connected via proxy and can estabilish TLS connection. + ''' + def __init__(self, raise_event, on_disconnect, idlequeue, estabilish_tls, + certs, proxy_dict=None): + ''' + :param proxy_dict: dictionary with proxy data as loaded from config file + ''' + NonBlockingTransport.__init__(self, raise_event, on_disconnect, idlequeue, + estabilish_tls, certs)
# queue with messages to be send self.sendqueue = [] @@ -269,869 +246,439 @@ # bytes remained from the last send message self.sendbuff = ''
- # time to wait for SOME stanza to come and then send keepalive - self.sendtimeout = 0 + self.proxy_dict = proxy_dict + self.on_remote_disconnect = self.disconnect
- # in case we want to something different than sending keepalives - self.on_timeout = None + def start_disconnect(self): + NonBlockingTransport.start_disconnect(self) + self.send('</stream:stream>', now=True) + self.disconnect()
- # writable, readable - keep state of the last pluged flags - # This prevents replug of same object with the same flags - self.writable = True - self.readable = False - self.ais = None + def connect(self, conn_5tuple, on_connect, on_connect_failure): + NonBlockingTransport.connect(self, conn_5tuple, on_connect, on_connect_failure) + log.info('NonBlockingTCP Connect :: About to connect to %s:%s' % (self.server, self.port))
- 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. ''' - self.idlequeue = owner.idlequeue - self.printed_error = False - if not self._server: - self._server=(self._owner.Server,5222) - if self.connect(self._server) is False: - return False - return True + try: + self._sock = socket.socket(*conn_5tuple[:3]) + except socket.error, (errnum, errstr): + self._on_connect_failure('NonBlockingTCP Connect: Error while creating socket:\ + %s %s' % (errnum, errstr)) + return
- def read_timeout(self): - if self.state == 0: - self.idlequeue.unplug_idle(self.fd) - if self.on_connect_failure: - self.on_connect_failure() - else: - if self.on_timeout: - self.on_timeout() - self.renew_send_timeout() + self._send = self._sock.send + self._recv = self._sock.recv + self.fd = self._sock.fileno() + + # we want to be notified when send is possible to connected socket because + # it means the TCP connection is estabilished + self._plug_idle(writable=True, readable=False) + self.peerhost = None
- def connect(self,server=None, proxy = None, secure = None): - ''' Try to establish connection. ''' - if not server: - server=self._server - else: - self._server = server - self._hostfqdn = self._server[0] - self.printed_error = False - self.state = 0 + # variable for errno symbol that will be found from exception raised from connect() + errnum = 0 + + # set timeout for TCP connecting - if nonblocking connect() fails, pollend + # is called. If if succeeds pollout is called. + self.idlequeue.set_read_timeout(self.fd, CONNECT_TIMEOUT_SECONDS) + try: - if len(server) == 2 and type(server[0]) in (str, unicode) and not \ - self.ais: - # FIXME: blocks here - self.ais = socket.getaddrinfo(server[0],server[1],socket.AF_UNSPEC,socket.SOCK_STREAM) - log.info('Found IPs: %s', self.ais) - else: - self.ais = (server,) - self.connect_to_next_ip() + self._sock.setblocking(False) + self._sock.connect((self.server,self.port)) + except Exception, (errnum, errstr): + pass + + if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): + # connecting in progress + log.info('After NB connect() of %s. "%s" raised => CONNECTING' % (id(self),errstr)) + self.tcp_connecting_started() return - except socket.gaierror, e: - log.info('Lookup failure for %s: %s[%s]', self.getName(), e[1], repr(e[0]), exc_info=True) - except: - log.error('Exception trying to connect to %s:', self.getName(), exc_info=True)
- if self.on_connect_failure: - self.on_connect_failure() + # if there was some other exception, call failure callback and unplug transport + # which will also remove read_timeouts for descriptor + self._on_connect_failure('Exception while connecting to %s:%s - %s %s' % + (self.server, self.port, errnum, errstr))
- def _plug_idle(self): - readable = self.state != 0 - if self.sendqueue or self.sendbuff: - writable = True + def _connect_to_proxy(self): + self.set_state(PROXY_CONNECTING) + if self.proxy_dict['type'] == 'socks5': + proxyclass = proxy_connectors.SOCKS5Connector + elif self.proxy_dict['type'] == 'http' : + proxyclass = proxy_connectors.HTTPCONNECTConnector + proxyclass( + send_method = self.send, + onreceive = self.onreceive, + old_on_receive = self.on_receive, + on_success = self._on_connect, + on_failure = self._on_connect_failure, + xmpp_server = self.proxy_dict['xmpp_server'], + proxy_creds = self.proxy_dict['credentials'] + ) + + def _on_connect(self): + ''' + Preceeds invoking of on_connect callback. TCP connection is already + estabilished by this time. + ''' + if self.estabilish_tls: + self.tls_init( + on_succ = lambda: NonBlockingTransport._on_connect(self), + on_fail = lambda: self._on_connect_failure('error while estabilishing TLS')) else: - writable = False - if self.writable != writable or self.readable != readable: - self.idlequeue.plug_idle(self, writable, readable) + NonBlockingTransport._on_connect(self)
- def pollout(self): - if self.state == 0: - self._do_connect() - return - self._do_send()
- def plugout(self): - ''' Disconnect from the remote server and unregister self.disconnected method from - the owner's dispatcher. ''' - self.disconnect() - self._owner.Connection = None - self._owner = None + def tls_init(self, on_succ, on_fail): + ''' + Estabilishes a TLS/SSL on TCP connection by plugging a NonBlockingTLS module + ''' + cacerts, mycerts = self.certs + result = tls_nb.NonBlockingTLS(cacerts, mycerts).PlugIn(self) + if result: + on_succ() + else: + on_fail()
def pollin(self): + '''called when receive on plugged socket is possible ''' + log.info('pollin called, state == %s' % self.get_state()) self._do_receive()
- def pollend(self, retry=False): - if not self.printed_error: - self.printed_error = True - try: - self._do_receive(errors_only=True) - except Exception: - log.error("pollend: Got exception from _do_receive:", exc_info=True) - conn_failure_cb = self.on_connect_failure - self.disconnect() - if conn_failure_cb: - conn_failure_cb(retry) + def pollout(self): + '''called when send to plugged socket is possible''' + log.info('pollout called, state == %s' % self.get_state())
- def disconnect(self): - if self.state == -2: # already disconnected + if self.get_state()==CONNECTING: + log.info('%s socket wrapper connected' % id(self)) + self.idlequeue.remove_timeout(self.fd) + self._plug_idle(writable=False, readable=False) + self.peerhost = self._sock.getsockname() + if self.proxy_dict: self._connect_to_proxy() + else: self._on_connect() return - self.state = -2 - self.sendqueue = None - self.remove_timeout() - try: - self._owner.disconnected() - except Exception: - pass - self.idlequeue.unplug_idle(self.fd) - sock = getattr(self, '_sock', None) - if sock: - try: - sock.shutdown(socket.SHUT_RDWR) - except socket.error, e: - if e[0] != errno.ENOTCONN: - log.error("Error shutting down socket for %s:", self.getName(), exc_info=True) - try: - sock.close() - except Exception: - log.error("Error closing socket for %s:", self.getName(), exc_info=True) - # socket descriptor cannot be (un)plugged anymore - self.fd = -1 - if self.on_disconnect: - self.on_disconnect() - self.on_connect_failure = None + self._do_send()
- def end_disconnect(self): - ''' force disconnect only if we are still trying to disconnect ''' - if self.state == -1: + def pollend(self): + '''called on error on TCP connection''' + log.info('pollend called, state == %s' % self.get_state()) + + if self.get_state()==CONNECTING: + self._on_connect_failure('Error during connect to %s:%s' % + (self.server, self.port)) + else: self.disconnect()
- def start_disconnect(self, to_send, on_disconnect): - self.on_disconnect = on_disconnect + def disconnect(self, do_callback=True): + if self.get_state() == DISCONNECTED: + return + self.set_state(DISCONNECTED) + self.idlequeue.unplug_idle(self.fd) + if 'NonBlockingTLS' in self.__dict__: self.NonBlockingTLS.PlugOut() + try: + self._sock.shutdown(socket.SHUT_RDWR) + self._sock.close() + except socket.error, (errnum, errstr): + log.error('Error while disconnecting socket: %s' % errstr) + self.fd = -1 + NonBlockingTransport.disconnect(self, do_callback)
- # flush the sendqueue - while self.sendqueue: - self._do_send() + def read_timeout(self): + ''' method called when timeout passed ''' + log.info('read_timeout called, state == %s' % self.get_state()) + if self.get_state()==CONNECTING: + # if read_timeout is called during connecting, connect() didn't end yet + # thus we have to call the tcp failure callback + self._on_connect_failure('Error during connect to %s:%s' % + (self.server, self.port)) + else: + NonBlockingTransport.read_timeout(self)
- self.sendqueue = [] - self.send(to_send) - self.send('</stream:stream>') - self.state = -1 # about to disconnect - self.idlequeue.set_alarm(self.end_disconnect, DISCONNECT_TIMEOUT_SECONDS) - def set_timeout(self, timeout): - if self.state >= 0 and self.fd > 0: - self.idlequeue.set_read_timeout(self.fd, timeout) + if self.get_state() != DISCONNECTED and self.fd != -1: + NonBlockingTransport.set_timeout(self, timeout) + else: + log.warn('set_timeout: TIMEOUT NOT SET: state is %s, fd is %s' % (self.get_state(), self.fd))
def remove_timeout(self): if self.fd: - self.idlequeue.remove_timeout(self.fd) + NonBlockingTransport.remove_timeout(self) + else: + log.warn('remove_timeout: no self.fd state is %s' % self.get_state())
- def onreceive(self, recv_handler): - ''' Sets the on_receive callback. Do not confuse it with - on_receive() method, which is the callback itself. + def send(self, raw_data, now=False): + '''Append raw_data to the queue of messages to be send. + If supplied data is unicode string, encode it to utf-8. + ''' + NonBlockingTransport.send(self, raw_data, now)
- If recv_handler==None, it tries to set that callback assuming that - our owner also has a Dispatcher object plugged in, to its - ProcessNonBlocking method.''' - if not recv_handler: - if hasattr(self._owner, 'Dispatcher'): - self.on_receive = self._owner.Dispatcher.ProcessNonBlocking - else: - self.on_receive = None - return - _tmp = self.on_receive - # make sure this cb is not overriden by recursive calls - if not recv_handler(None) and _tmp == self.on_receive: - self.on_receive = recv_handler + r = self.encode_stanza(raw_data)
- def _do_receive(self, errors_only=False): - ''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.''' - ERR_DISCONN = -2 # Misc error signifying that we got disconnected - ERR_OTHER = -1 # Other error - received = None - errnum = 0 - errtxt = 'No Error Set' - try: - # get as many bites, as possible, but not more than RECV_BUFSIZE - received = self._recv(RECV_BUFSIZE) - except (socket.error, socket.herror, socket.gaierror), e: - log.debug("_do_receive: got %s:", e.__class__, exc_info=True) - #traceback.print_exc() - #print "Current Stack:" - #traceback.print_stack() - errnum = e[0] - errtxt = str(errnum) + ':' + e[1] - except socket.sslerror, e: - log.error("_do_receive: got unknown %s:", e.__class__, exc_info=True) - #traceback.print_exc() - #print "Current Stack:" - #traceback.print_stack() - errnum = ERR_OTHER - errtxt = repr("socket.sslerror: " + e.args) - except SSLWrapper.Error, e: - log.debug("Caught: %s", str(e)) - errnum = gattr(e, 'errno', ERR_OTHER) - if not errnum: errnum = ERR_OTHER # unset, but we must put a status - errtxt = gattr(e, 'strerror') or repr(e.args) + if now: + self.sendqueue.insert(0, r) + self._do_send() + else: + self.sendqueue.append(r)
- if received == '': - errnum = ERR_DISCONN - errtxt = "Connection closed unexpectedly" + self._plug_idle(writable=True, readable=True)
- if errnum in (ERR_DISCONN, errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN): - log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum) - self.printed_error = True - if not errors_only: - self.pollend(retry=(errnum in (ERR_DISCONN, errno.ECONNRESET))) - # don't process result, because it will raise an error - return + def encode_stanza(self, stanza): + if isinstance(stanza, unicode): + stanza = stanza.encode('utf-8') + elif not isinstance(stanza, str): + stanza = ustr(stanza).encode('utf-8') + return stanza
- if received is None: - if self.state == 0 and errnum == errno.ECONNREFUSED: - # We tried to connect to a port that did't listen. - log.error("Connection to %s refused: %s [%d]", self.getName(), errtxt, errnum) - self.pollend(retry=True) - return - elif errnum != 0: - self.DEBUG(errtxt, 'error') - log.error("Connection to %s lost: %s [%d]", self.getName(), errtxt, errnum) - self._owner.disconnected() - self.printed_error = True - if not errors_only and self.state >= 0: - self.pollend(retry=True) - return - received = '' + def _plug_idle(self, writable, readable): + ''' + Plugs file descriptor of socket to Idlequeue. Plugged socket + will be watched for "send possible" or/and "recv possible" events. pollin() + callback is invoked on "recv possible", pollout() on "send_possible". + Plugged socket will always be watched for "error" event - in that case, + pollend() is called. + ''' + log.info('Plugging fd %d, W:%s, R:%s' % (self.fd, writable, readable)) + self.idlequeue.plug_idle(self, writable, readable)
- if errors_only or self.state < 0: - return
- # we have received some bites, stop the timeout! - self.renew_send_timeout() - if self.on_receive: - if received.strip(): - self.DEBUG(received, 'got') - if hasattr(self._owner, 'Dispatcher'): - self._owner.Dispatcher.Event('', DATA_RECEIVED, received) - self.on_receive(received) - else: - # This should never happed, so we need the debug - self.DEBUG('Unhandled data received: %s' % received,'got') - self.disconnect() - if self.on_connect_failure: - self.on_connect_failure() - return True - def _do_send(self): + ''' + Called when send() to connected socket is possible. First message from + sendqueue will be sent. + ''' if not self.sendbuff: if not self.sendqueue: - return None # nothing to send + log.warn('calling send on empty buffer and queue') + self._plug_idle(writable=False, readable=True) + return None self.sendbuff = self.sendqueue.pop(0) - self.sent_data = self.sendbuff try: send_count = self._send(self.sendbuff) if send_count: + sent_data = self.sendbuff[:send_count] self.sendbuff = self.sendbuff[send_count:] - if not self.sendbuff and not self.sendqueue: - if self.state < 0: - self.idlequeue.unplug_idle(self.fd) - self._on_send() - self.disconnect() - return - # we are not waiting for write - self._plug_idle() - self._on_send() - except socket.error, e: - if e[0] == socket.SSL_ERROR_WANT_WRITE: - return True - log.error("_do_send:", exc_info=True) - #traceback.print_exc() - if self.state < 0: - self.disconnect() - return - if self._on_send_failure: - self._on_send_failure() - return - return True + self._plug_idle( + writable=((self.sendqueue!=[]) or (self.sendbuff!='')), + readable=True) + self.raise_event(DATA_SENT, sent_data)
- def connect_to_next_ip(self): - if self.state != 0: - return - if len(self.ais) == 0: - if self.on_connect_failure: - self.on_connect_failure() - return - ai = self.ais.pop(0) - log.info('Trying to connect to %s:%s', ai[4][0], ai[4][1]) - try: - self._sock = socket.socket(*ai[:3]) - self._sock.setblocking(False) - self._server=ai[4] except socket.error, e: - errnum, errstr = e + log.error('_do_send:', exc_info=True) + traceback.print_exc() + self.disconnect()
- # Ignore "Socket already connected". - # FIXME: This happens when we switch an already - # connected socket to SSL (STARTTLS). Instead of - # ignoring the error, the socket should only be - # connected to once. See #2846 and #3396. - workaround = (errno.EALREADY, 10056, 56) - - # 10035 - winsock equivalent of EINPROGRESS - if errnum not in (errno.EINPROGRESS, 10035) + workaround: - log.error('Could not connect to %s: %s [%s]', ai[4][0], errnum, - errstr, exc_info=True) - #traceback.print_exc() - self.connect_to_next_ip() - return - self.fd = self._sock.fileno() - self.idlequeue.plug_idle(self, True, False) - self._send = self._sock.send - self._recv = self._sock.recv - self.set_timeout(CONNECT_TIMEOUT_SECONDS) - self._do_connect() - - def _do_connect(self): + def _do_receive(self): + ''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.''' + received = None errnum = 0 - if self.state != 0: - return + errstr = 'No Error Set'
try: - self._sock.connect(self._server) - except Exception, ee: - (errnum, errstr) = ee - # in progress, or would block - if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): - return - # 10056 - already connected, only on win32 - # code 'WS*' is not available on GNU, so we use its numeric value - elif errnum not in (0, 10056, errno.EISCONN): - log.error('Could not connect to %s: %s [%s]', self._server[0], errnum, - errstr) - self.connect_to_next_ip() - return - self.remove_timeout() - self._owner.Connection=self - self.state = 1 + # get as many bites, as possible, but not more than RECV_BUFSIZE + received = self._recv(RECV_BUFSIZE) + except socket.error, (errnum, errstr): + log.info("_do_receive: got %s:" % received , exc_info=True) + except tls_nb.SSLWrapper.Error, e: + log.info("_do_receive, caught SSL error, got %s:" % received , exc_info=True) + errnum, errstr = e.exc
- self._plug_idle() - if self.on_connect: - self.on_connect() - self.on_connect = None + if received == '': errstr = 'zero bytes on recv'
- def send(self, raw_data, now = False): - '''Append raw_data to the queue of messages to be send. - If supplied data is unicode string, encode it to utf-8. - ''' - - if self.state <= 0: + if (self.ssl_lib is None and received == '') or \ + (self.ssl_lib == tls_nb.PYSTDLIB and errnum == 8 ) or \ + (self.ssl_lib == tls_nb.PYOPENSSL and errnum == -1 ): + # 8 in stdlib: errstr == EOF occured in violation of protocol + # -1 in pyopenssl: errstr == Unexpected EOF + log.info("Disconnected by remote server: #%s, %s" % (errnum, errstr)) + self.on_remote_disconnect() return - r = raw_data - if isinstance(r, unicode): - r = r.encode('utf-8') - elif not isinstance(r, str): - r = ustr(r).encode('utf-8') - if now: - self.sendqueue.insert(0, r) - self._do_send() - else: - self.sendqueue.append(r)
- self._plug_idle()
- def _on_send(self): - if self.sent_data and self.sent_data.strip(): - self.DEBUG(self.sent_data,'sent') - if hasattr(self._owner, 'Dispatcher'): - self._owner.Dispatcher.Event('', DATA_SENT, self.sent_data) - self.sent_data = None + if errnum: + log.error("Connection to %s:%s lost: %s %s" % ( self.server, self.port, errnum, errstr), exc_info=True) + self.disconnect() + return
- def _on_send_failure(self): - self.DEBUG("Socket error while sending data",'error') - self._owner.disconnected() - self.sent_data = None + # this branch is for case of non-fatal SSL errors - None is returned from + # recv() but no errnum is set
- def set_send_timeout(self, timeout, on_timeout): - self.sendtimeout = timeout - if self.sendtimeout > 0: - self.on_timeout = on_timeout - else: - self.on_timeout = None + if received is None: + return
- def renew_send_timeout(self): - if self.on_timeout and self.sendtimeout > 0: - self.set_timeout(self.sendtimeout) + # we have received some bytes, stop the timeout! + self.renew_send_timeout() + # pass received data to owner + if self.on_receive: + self.raise_event(DATA_RECEIVED, received) + self._on_receive(received) else: - self.remove_timeout() + # This should never happen, so we need the debug. (If there is no handler + # on receive specified, data are passed to Dispatcher.ProcessNonBlocking) + log.error('SOCKET %s Unhandled data received: %s' % (id(self), received)) + self.disconnect()
- def getHost(self): - ''' Return the 'host' value that is connection is [will be] made to.''' - return self._server[0] + def _on_receive(self, data): + ''' preceeds on_receive callback. It peels off and checks HTTP headers in + class, in here it just calls the callback.''' + self.on_receive(data)
- def getName(self): - ''' Return the server's name, or 'getHost()' if not available.''' - retval = None - try: - retval = gattr(self._owner, 'name') - except Exception: - pass - if retval: return retval - return self.getHost()
- def getPort(self): - ''' Return the 'port' value that is connection is [will be] made to.''' - return self._server[1] +class NonBlockingHTTP(NonBlockingTCP): + ''' + Socket wrapper that creates HTTP message out of sent data and peels-off + HTTP headers from incoming messages + '''
-DBG_NONBLOCKINGTLS = "NonBlockingTLS" + def __init__(self, raise_event, on_disconnect, idlequeue, estabilish_tls, + certs, on_http_request_possible, on_persistent_fallback, http_dict, + proxy_dict = None): + ''' + :param on_http_request_possible: method to call when HTTP request to socket + owned by transport is possible. + :param on_persistent_fallback: callback called when server ends TCP + connection. It doesn't have to be fatal for HTTP session. + :param http_dict: dictionary with data for HTTP request and headers + '''
-class NonBlockingTLS(PlugIn): - ''' TLS connection used to encrypts already estabilished tcp connection.''' + NonBlockingTCP.__init__(self, raise_event, on_disconnect, idlequeue, + estabilish_tls, certs, proxy_dict)
- # from ssl.h (partial extract) - ssl_h_bits = { "SSL_ST_CONNECT": 0x1000, "SSL_ST_ACCEPT": 0x2000, - "SSL_CB_LOOP": 0x01, "SSL_CB_EXIT": 0x02, - "SSL_CB_READ": 0x04, "SSL_CB_WRITE": 0x08, - "SSL_CB_ALERT": 0x4000, - "SSL_CB_HANDSHAKE_START": 0x10, "SSL_CB_HANDSHAKE_DONE": 0x20} + self.http_protocol, self.http_host, self.http_path = urisplit(http_dict['http_uri']) + if self.http_protocol is None: + self.http_protocol = 'http' + if self.http_path == '': + self.http_path = '/' + self.http_port = http_dict['http_port'] + self.http_version = http_dict['http_version'] + self.http_persistent = http_dict['http_persistent'] + self.add_proxy_headers = http_dict['add_proxy_headers'] + if http_dict.has_key('proxy_user') and http_dict.has_key('proxy_pass'): + self.proxy_user, self.proxy_pass = http_dict['proxy_user'], http_dict['proxy_pass'] + else: + self.proxy_user, self.proxy_pass = None, None
- def PlugIn(self, owner, now=0, on_tls_start = None): - ''' 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). + # buffer for partial responses + self.recvbuff = '' + self.expected_length = 0 + self.pending_requests = 0 + self.on_http_request_possible = on_http_request_possible + self.last_recv_time = 0 + self.close_current_connection = False + self.on_remote_disconnect = lambda: on_persistent_fallback(self) + + def http_send(self, raw_data, now=False): + self.send(self.build_http_message(raw_data), now) + + def _on_receive(self,data): ''' - if 'NonBlockingTLS' in owner.__dict__: - return # Already enabled. - PlugIn.PlugIn(self, owner) - self.DBG_LINE = DBG_NONBLOCKINGTLS - owner.debug_flags.append(DBG_NONBLOCKINGTLS) - self.on_tls_start = on_tls_start - if now: + Preceeds passing received data to owner class. Gets rid of HTTP headers + and checks them. + ''' + if self.get_state() == PROXY_CONNECTING: + NonBlockingTCP._on_receive(self, data) + return + if not self.recvbuff: + # recvbuff empty - fresh HTTP message was received try: - res = self._startSSL() - except Exception: - log.error("PlugIn: while trying _startSSL():", exc_info=True) - #traceback.print_exc() - self._owner.socket.pollend() + statusline, headers, self.recvbuff = self.parse_http_message(data) + except ValueError: + self.disconnect() return - self.tls_start() - return res - if self._owner.Dispatcher.Stream.features: - try: - self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) - except NodeProcessed: - pass + if statusline[1] != '200': + log.error('HTTP Error: %s %s' % (statusline[1], statusline[2])) + self.disconnect() + return + self.expected_length = int(headers['Content-Length']) + if headers.has_key('Connection') and headers['Connection'].strip()=='close': + self.close_current_connection = True else: - self._owner.RegisterHandlerOnce('features',self.FeaturesHandler, xmlns=NS_STREAMS) - self.starttls = None + #sth in recvbuff - append currently received data to HTTP msg in buffer + self.recvbuff = '%s%s' % (self.recvbuff, data)
- 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.''' - # if dispatcher is not plugged we cannot (un)register handlers - if 'Dispatcher' in self._owner.__dict__: - self._owner.UnregisterHandler('features', self.FeaturesHandler,xmlns=NS_STREAMS) - self._owner.Dispatcher.PlugOut() - self._owner = None - - def tls_start(self): - if self.on_tls_start: - self.on_tls_start() - self.on_tls_start = None - - 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') - self.tls_start() + if self.expected_length > len(self.recvbuff): + # If we haven't received the whole HTTP mess yet, let's end the thread. + # It will be finnished from one of following recvs on plugged socket. + log.info('not enough bytes in HTTP response - %d expected, %d got' % + (self.expected_length, len(self.recvbuff))) 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.send('<starttls xmlns="%s"/>' % NS_TLS) - self.tls_start() - raise NodeProcessed
- def _dumpX509(self, cert, stream=sys.stderr): - print >> stream, "Digest (SHA-1):", cert.digest("sha1") - print >> stream, "Digest (MD5):", cert.digest("md5") - print >> stream, "Serial #:", cert.get_serial_number() - print >> stream, "Version:", cert.get_version() - print >> stream, "Expired:", torf(cert.has_expired(), "Yes", "No") - print >> stream, "Subject:" - self._dumpX509Name(cert.get_subject(), stream) - print >> stream, "Issuer:" - self._dumpX509Name(cert.get_issuer(), stream) - self._dumpPKey(cert.get_pubkey(), stream) + # everything was received + httpbody = self.recvbuff
- def _dumpX509Name(self, name, stream=sys.stderr): - print >> stream, "X509Name:", str(name) + self.recvbuff='' + self.expected_length=0
- def _dumpPKey(self, pkey, stream=sys.stderr): - typedict = {OpenSSL.crypto.TYPE_RSA: "RSA", OpenSSL.crypto.TYPE_DSA: "DSA"} - print >> stream, "PKey bits:", pkey.bits() - print >> stream, "PKey type: %s (%d)" % (typedict.get(pkey.type(), "Unknown"), pkey.type()) + if not self.http_persistent or self.close_current_connection: + # not-persistent connections disconnect after response + self.disconnect(do_callback=False) + self.close_current_connection = False + self.last_recv_time = time.time() + self.on_receive(data=httpbody, socket=self) + self.on_http_request_possible()
- def _startSSL(self): - ''' Immidiatedly switch socket to TLS mode. Used internally.''' - log.debug("_startSSL called") - if USE_PYOPENSSL: return self._startSSL_pyOpenSSL() - return self._startSSL_stdlib()
- def _startSSL_pyOpenSSL(self): - #log.debug("_startSSL_pyOpenSSL called, thread id: %s", str(thread.get_ident())) - log.debug("_startSSL_pyOpenSSL called") - tcpsock = self._owner.Connection - # FIXME: should method be configurable? - # Some gmail server don't support TLSv1, but only SSLv3, so use method - # that allow SSLv2, v3 and TLSv1 - #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) - 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(common.gajim.DATA_DIR, 'other', 'cacerts.pem') - try: - tcpsock._sslContext.load_verify_locations(cacerts) - except Exception: - log.warning('Unable to load SSL certificats from file %s' % \ - os.path.abspath(cacerts)) - # load users certs - if os.path.isfile(common.gajim.MY_CACERTS): - store = tcpsock._sslContext.get_cert_store() - f = open(common.gajim.MY_CACERTS) - lines = f.readlines() - i = 0 - begin = -1 - for line in lines: - if 'BEGIN CERTIFICATE' in line: - begin = i - elif 'END CERTIFICATE' in line and begin > -1: - cert = ''.join(lines[begin:i+2]) - try: - X509cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert) - store.add_cert(X509cert) - except OpenSSL.crypto.Error, exception_obj: - log.warning('Unable to load a certificate from file %s: %s' %\ - (common.gajim.MY_CACERTS, exception_obj.args[0][0][2])) - except: - log.warning( - 'Unknown error while loading certificate from file %s' % \ - common.gajim.MY_CACERTS) - begin = -1 - i += 1 - tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) - tcpsock._sslObj.set_connect_state() # set to client mode + def build_http_message(self, httpbody, method='POST'): + ''' + Builds http message with given body. + Values for headers and status line fields are taken from class variables. + ''' + absolute_uri = '%s://%s:%s%s' % (self.http_protocol, self.http_host, + self.http_port, self.http_path) + headers = ['%s %s %s' % (method, absolute_uri, self.http_version), + 'Host: %s:%s' % (self.http_host, self.http_port), + 'User-Agent: Gajim', + 'Content-Type: text/xml; charset=utf-8', + 'Content-Length: %s' % len(str(httpbody))] + if self.add_proxy_headers: + headers.append('Proxy-Connection: keep-alive') + headers.append('Pragma: no-cache') + if self.proxy_user and self.proxy_pass: + credentials = '%s:%s' % (self.proxy_user, self.proxy_pass) + credentials = base64.encodestring(credentials).strip() + headers.append('Proxy-Authorization: Basic %s' % credentials) + else: + headers.append('Connection: Keep-Alive') + headers.append('\r\n') + headers = '\r\n'.join(headers) + return('%s%s\r\n' % (headers, httpbody))
- wrapper = PyOpenSSLWrapper(tcpsock._sslObj) - tcpsock._recv = wrapper.recv - tcpsock._send = wrapper.send + 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', 1) + header = header.split('\n') + statusline = header[0].split(' ', 2) + header = header[1:] + headers = {} + for dummy in header: + row = dummy.split(' ',1) + headers[row[0][:-1]] = row[1] + return (statusline, headers, httpbody)
- log.debug("Initiating handshake...") - # FIXME: Figure out why _connect_success is called before the - # SSL handshake is completed in STARTTLS mode. See #2838. - tcpsock._sslObj.setblocking(True) - try: - self.starttls='in progress' - tcpsock._sslObj.do_handshake() - # Errors are handeled in _do_receive function - except Exception: - pass - tcpsock._sslObj.setblocking(False) - log.debug("Synchronous handshake completed") - #log.debug("Async handshake started...")
- # fake it, for now - self.starttls='success' - - def _startSSL_stdlib(self): - log.debug("_startSSL_stdlib called") - tcpsock=self._owner.Connection - tcpsock._sock.setblocking(True) - tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) - tcpsock._sock.setblocking(False) - tcpsock._sslIssuer = tcpsock._sslObj.issuer() - tcpsock._sslServer = tcpsock._sslObj.server() - wrapper = StdlibSSLWrapper(tcpsock._sslObj, tcpsock._sock) - tcpsock._recv = wrapper.recv - tcpsock._send = wrapper.send - self.starttls='success' - - def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): - # Exceptions can't propagate up through this callback, so print them here. - try: - self._owner.Connection.ssl_fingerprint_sha1 = cert.digest('sha1') - if errnum == 0: - return True - self._owner.Connection.ssl_errnum = errnum - self._owner.Connection.ssl_cert_pem = OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, cert) - return True - except Exception: - log.error("Exception caught in _ssl_info_callback:", exc_info=True) - traceback.print_exc() # Make sure something is printed, even if log is disabled. - - def 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') - try: - self._startSSL() - except Exception: - log.error("StartTLSHandler:", exc_info=True) - #traceback.print_exc() - self._owner.socket.pollend() - return - self._owner.Dispatcher.PlugOut() - dispatcher_nb.Dispatcher().PlugIn(self._owner) - - -class NBHTTPPROXYsocket(NonBlockingTcp): - ''' This class can be used instead of transports.HTTPPROXYsocket - 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). - +class NonBlockingHTTPBOSH(NonBlockingHTTP): ''' - def __init__(self, on_connect =None, on_proxy_failure=None, on_connect_failure = None,proxy = None,server = None,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. ''' - self.on_connect_proxy = on_connect - self.on_proxy_failure = on_proxy_failure - self.on_connect_failure = on_connect_failure - NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure, server, use_srv) - self.DBG_LINE=DBG_CONNECT_PROXY - self.server = server - self.proxy=proxy - - def plugin(self, owner): - ''' Starts connection. Used interally. Returns non-empty string on success.''' - owner.debug_flags.append(DBG_CONNECT_PROXY) - NonBlockingTcp.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. ''' - NonBlockingTcp.connect(self, (self.proxy['host'], self.proxy['port'])) - - def _on_tcp_connect(self): - 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 'user' in self.proxy and 'password' in self.proxy: - 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.onreceive(self._on_headers_sent) - self.send('\r\n'.join(connector)) - - def _on_headers_sent(self, reply): - if reply is None: - return - self.reply = reply.replace('\r', '') - try: - proto, code, desc = reply.split('\n')[0].split(' ', 2) - except Exception: - log.error("_on_headers_sent:", exc_info=True) - #traceback.print_exc() - self.on_proxy_failure('Invalid proxy reply') - return - if code != '200': - self.DEBUG('Invalid proxy reply: %s %s %s' % (proto, code, desc),'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if len(reply) != 2: - pass - self.onreceive(self._on_proxy_auth) - - def _on_proxy_auth(self, reply): - if self.reply.find('\n\n') == -1: - if reply is None: - self.on_proxy_failure('Proxy authentification failed') - return - if reply.find('\n\n') == -1: - self.reply += reply.replace('\r', '') - self.on_proxy_failure('Proxy authentification failed') - return - self.DEBUG('Authentification successfull. Jabber server contacted.','ok') - if self.on_connect_proxy: - self.on_connect_proxy() - - 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 NBSOCKS5PROXYsocket(NonBlockingTcp): - '''SOCKS5 proxy connection class. Uses TCPsocket as the base class - redefines only connect method. Allows to use SOCKS5 proxies with - (optionally) simple authentication (only USERNAME/PASSWORD auth). + Class for BOSH HTTP connections. Slightly redefines HTTP transport by calling + bosh bodytag generating callback before putting data on wire. ''' - def __init__(self, on_connect = None, on_proxy_failure = None, - on_connect_failure = None, proxy = None, server = None, 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. ''' - self.on_connect_proxy = on_connect - self.on_proxy_failure = on_proxy_failure - self.on_connect_failure = on_connect_failure - NonBlockingTcp.__init__(self, self._on_tcp_connect, on_connect_failure, - server, use_srv) - self.DBG_LINE=DBG_CONNECT_PROXY - self.server = server - self.proxy = proxy - self.ipaddr = None
- def plugin(self, owner): - ''' Starts connection. Used interally. Returns non-empty string on - success.''' - owner.debug_flags.append(DBG_CONNECT_PROXY) - NonBlockingTcp.plugin(self, owner) + def set_stanza_build_cb(self, build_cb): + self.build_cb = build_cb
- 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. - ''' - NonBlockingTcp.connect(self, (self.proxy['host'], self.proxy['port'])) - - def _on_tcp_connect(self): - self.DEBUG('Proxy server contacted, performing authentification', 'start') - if 'user' in self.proxy and 'password' in self.proxy: - to_send = '\x05\x02\x00\x02' - else: - to_send = '\x05\x01\x00' - self.onreceive(self._on_greeting_sent) - self.send(to_send) - - def _on_greeting_sent(self, reply): - if reply is None: + def _do_send(self): + if self.state == PROXY_CONNECTING: + NonBlockingTCP._do_send(self) return - if len(reply) != 2: - self.on_proxy_failure('Invalid proxy reply') - return - if reply[0] != '\x05': - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if reply[1] == '\x00': - return self._on_proxy_auth('\x01\x00') - elif reply[1] == '\x02': - to_send = '\x01' + chr(len(self.proxy['user'])) + self.proxy['user'] +\ - chr(len(self.proxy['password'])) + self.proxy['password'] - self.onreceive(self._on_proxy_auth) - self.send(to_send) - else: - if reply[1] == '\xff': - self.DEBUG('Authentification to proxy impossible: no acceptable ' - 'auth method', 'error') - self._owner.disconnected() - self.on_proxy_failure('Authentification to proxy impossible: no ' - 'acceptable authentification method') - return - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return + if not self.sendbuff: + stanza = self.build_cb(socket=self) + stanza = self.encode_stanza(stanza) + stanza = self.build_http_message(httpbody=stanza) + self.sendbuff = stanza + NonBlockingTCP._do_send(self)
- def _on_proxy_auth(self, reply): - if reply is None: - return - if len(reply) != 2: - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if reply[0] != '\x01': - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if reply[1] != '\x00': - self.DEBUG('Authentification to proxy failed', 'error') - self._owner.disconnected() - self.on_proxy_failure('Authentification to proxy failed') - return - self.DEBUG('Authentification successfull. Jabber server contacted.','ok') - # Request connection - req = "\x05\x01\x00" - # If the given destination address is an IP address, we'll - # use the IPv4 address request even if remote resolving was specified. - try: - self.ipaddr = socket.inet_aton(self.server[0]) - req = req + "\x01" + self.ipaddr - except socket.error: - # Well it's not an IP number, so it's probably a DNS name. -# if self.__proxy[3]==True: - # Resolve remotely - self.ipaddr = None - req = req + "\x03" + chr(len(self.server[0])) + self.server[0] -# else: -# # Resolve locally -# self.ipaddr = socket.inet_aton(socket.gethostbyname(self.server[0])) -# req = req + "\x01" + ipaddr - req = req + struct.pack(">H",self.server[1]) - self.onreceive(self._on_req_sent) - self.send(req) - - def _on_req_sent(self, reply): - if reply is None: - return - if len(reply) < 10: - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if reply[0] != '\x05': - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - if reply[1] != "\x00": - # Connection failed - self._owner.disconnected() - if ord(reply[1])<9: - errors = ['general SOCKS server failure', - 'connection not allowed by ruleset', - 'Network unreachable', - 'Host unreachable', - 'Connection refused', - 'TTL expired', - 'Command not supported', - 'Address type not supported' - ] - txt = errors[ord(reply[1])-1] - else: - txt = 'Invalid proxy reply' - self.DEBUG(txt, 'error') - self.on_proxy_failure(txt) - return - # Get the bound address/port - elif reply[3] == "\x01": - pass # begin, end = 3, 7 - elif reply[3] == "\x03": - pass # begin, end = 4, 4 + reply[4] - else: - self.DEBUG('Invalid proxy reply', 'error') - self._owner.disconnected() - self.on_proxy_failure('Invalid proxy reply') - return - - if self.on_connect_proxy: - self.on_connect_proxy() - - 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) - # vim: se ts=3:
Modified: branches/osx_newbuildsys/src/common/zeroconf/client_zeroconf.py =================================================================== --- branches/osx_newbuildsys/src/common/zeroconf/client_zeroconf.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/zeroconf/client_zeroconf.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -23,6 +23,7 @@ from common.xmpp import dispatcher_nb, simplexml from common.xmpp.client import * from common.xmpp.simplexml import ustr +from common.xmpp.transports_nb import DATA_RECEIVED, DATA_SENT from common.zeroconf import zeroconf
from common.xmpp.protocol import * @@ -36,8 +37,6 @@ from common.zeroconf import roster_zeroconf
MAX_BUFF_LEN = 65536 -DATA_RECEIVED = 'DATA RECEIVED' -DATA_SENT = 'DATA SENT' TYPE_SERVER, TYPE_CLIENT = range(2)
# wait XX sec to establish a connection @@ -120,6 +119,7 @@ on_ok=None, on_not_ok=None): self._owner = self self.Namespace = 'jabber:client' + self.protocol_type = 'XMPP' self.defaultNamespace = self.Namespace self._component = 0 self._registered_name = None @@ -130,16 +130,7 @@ self.Server = host self.on_ok = on_ok self.on_not_ok = on_not_ok - self.DBG = 'client' self.Connection = None - if gajim.verbose: - debug = ['always', 'nodebuilder'] - else: - debug = [] - self._DEBUG = Debug.Debug(debug) - self.DEBUG = self._DEBUG.Show - self.debug_flags = self._DEBUG.debug_flags - self.debug_flags.append(self.DBG) self.sock_hash = None if _sock: self.sock_type = TYPE_SERVER @@ -158,7 +149,7 @@ self.conn_holder.add_connection(self, self.Server, port, self.to) # count messages in queue for val in self.stanzaqueue: - is_message = val[1] + stanza, is_message = val if is_message: if self.fd == -1: if on_not_ok: @@ -204,8 +195,6 @@ 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 if self.sock_type == TYPE_CLIENT: self.send_stream_header() @@ -223,8 +212,7 @@
def _check_stream_start(self, ns, tag, attrs): if ns != NS_STREAMS or tag != 'stream': - self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' \ - % (tag, ns), 'error') + log.error('Incorrect stream start: (%s,%s).Terminating!' % (tag, ns), 'error') self.Connection.disconnect() if self.on_not_ok: self.on_not_ok('Connection to host could not be established: Incorrect answer from server.') @@ -301,7 +289,6 @@ IdleObject.__init__(self) self._owner = client PlugIn.__init__(self) - self.DBG_LINE = 'socket' self.sendqueue = [] self.sendbuff = None self.buff_is_message = False @@ -474,13 +461,13 @@ if self._owner.sock_type == TYPE_CLIENT: self.set_timeout(ACTIVITY_TIMEOUT_SECONDS) if received.strip(): - self.DEBUG(received, 'got') + log.debug('received: %s', received) if hasattr(self._owner, 'Dispatcher'): self._owner.Dispatcher.Event('', DATA_RECEIVED, received) self.on_receive(received) else: # This should never happed, so we need the debug - self.DEBUG('Unhandled data received: %s' % received,'error') + log.error('Unhandled data received: %s' % received) self.disconnect() return True
@@ -543,7 +530,7 @@
def _on_send(self): if self.sent_data and self.sent_data.strip(): - self.DEBUG(self.sent_data,'sent') + log.debug('sent: %s' % self.sent_data) if hasattr(self._owner, 'Dispatcher'): self._owner.Dispatcher.Event('', DATA_SENT, self.sent_data) self.sent_data = None @@ -552,7 +539,7 @@ self.buff_is_message = False
def _on_send_failure(self): - self.DEBUG("Socket error while sending data",'error') + log.error('Socket error while sending data') self._owner.disconnected() self.sent_data = None
Modified: branches/osx_newbuildsys/src/common/zeroconf/connection_zeroconf.py =================================================================== --- branches/osx_newbuildsys/src/common/zeroconf/connection_zeroconf.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/common/zeroconf/connection_zeroconf.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -138,6 +138,7 @@ if event in gajim.handlers: gajim.handlers[event](self.name, data)
+ def _reconnect(self): # Do not try to reco while we are already trying self.time_to_reconnect = None @@ -472,20 +473,21 @@ else: kind = 'single_msg_sent' gajim.logger.write(kind, jid, log_msg) - + self.dispatch('MSGSENT', (jid, msg, keyID))
def on_send_not_ok(reason): reason += ' ' + _('Your message could not be sent.') self.dispatch('MSGERROR', [jid, -1, reason, None, None, session]) - ret = self.connection.send(msg_iq, msg is not None, on_ok=on_send_ok, on_not_ok=on_send_not_ok) if ret == -1: # Contact Offline self.dispatch('MSGERROR', [jid, -1, _('Contact is offline. Your message could not be sent.'), None, None, session]) return ret + return ret
+ def send_stanza(self, stanza): # send a stanza untouched if not self.connection: @@ -571,9 +573,9 @@
def _event_dispatcher(self, realm, event, data): if 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)) elif event == common.xmpp.transports.DATA_ERROR: thread_id = data[1]
Modified: branches/osx_newbuildsys/src/config.py =================================================================== --- branches/osx_newbuildsys/src/config.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/config.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -1130,10 +1130,31 @@ self.proxies_treeview = self.xml.get_widget('proxies_treeview') self.proxyname_entry = self.xml.get_widget('proxyname_entry') self.proxytype_combobox = self.xml.get_widget('proxytype_combobox') + self.init_list() self.xml.signal_autoconnect(self) self.window.show_all() + # hide the BOSH fields by default + self.show_bosh_fields()
+ def show_bosh_fields(self, show=True): + if show: + self.xml.get_widget('boshuri_entry').show() + self.xml.get_widget('boshport_entry').show() + self.xml.get_widget('boshuri_label').show() + self.xml.get_widget('boshport_label').show() + self.xml.get_widget('boshuseproxy_checkbutton').show() + else: + cb = self.xml.get_widget('boshuseproxy_checkbutton') + cb.hide() + cb.set_active(True) + self.on_boshuseproxy_checkbutton_toggled(cb) + self.xml.get_widget('boshuri_entry').hide() + self.xml.get_widget('boshport_entry').hide() + self.xml.get_widget('boshuri_label').hide() + self.xml.get_widget('boshport_label').hide() + + def fill_proxies_treeview(self): model = self.proxies_treeview.get_model() model.clear() @@ -1187,9 +1208,18 @@
def on_useauth_checkbutton_toggled(self, widget): act = widget.get_active() + proxy = self.proxyname_entry.get_text().decode('utf-8') + gajim.config.set_per('proxies', proxy, 'useauth', act) self.xml.get_widget('proxyuser_entry').set_sensitive(act) self.xml.get_widget('proxypass_entry').set_sensitive(act)
+ def on_boshuseproxy_checkbutton_toggled(self, widget): + act = widget.get_active() + proxy = self.proxyname_entry.get_text().decode('utf-8') + gajim.config.set_per('proxies', proxy, 'bosh_useproxy', act) + self.xml.get_widget('proxyhost_entry').set_sensitive(act) + self.xml.get_widget('proxyport_entry').set_sensitive(act) + def on_proxies_treeview_cursor_changed(self, widget): #FIXME: check if off proxy settings are correct (see # http://trac.gajim.org/changeset/1921#file2 line 1221 @@ -1202,19 +1232,33 @@ proxyport_entry = self.xml.get_widget('proxyport_entry') proxyuser_entry = self.xml.get_widget('proxyuser_entry') proxypass_entry = self.xml.get_widget('proxypass_entry') + boshuri_entry = self.xml.get_widget('boshuri_entry') + boshport_entry = self.xml.get_widget('boshport_entry') useauth_checkbutton = self.xml.get_widget('useauth_checkbutton') + boshuseproxy_checkbutton = self.xml.get_widget('boshuseproxy_checkbutton') proxyhost_entry.set_text('') proxyport_entry.set_text('') proxyuser_entry.set_text('') proxypass_entry.set_text('') - useauth_checkbutton.set_active(False) - self.on_useauth_checkbutton_toggled(useauth_checkbutton) + boshuri_entry.set_text('') + + #boshuseproxy_checkbutton.set_active(False) + #self.on_boshuseproxy_checkbutton_toggled(boshuseproxy_checkbutton) + + #useauth_checkbutton.set_active(False) + #self.on_useauth_checkbutton_toggled(useauth_checkbutton) + if proxy == _('None'): # special proxy None + self.show_bosh_fields(False) self.proxyname_entry.set_editable(False) self.xml.get_widget('remove_proxy_button').set_sensitive(False) self.xml.get_widget('proxytype_combobox').set_sensitive(False) self.xml.get_widget('proxy_table').set_sensitive(False) else: + proxytype = gajim.config.get_per('proxies', proxy, 'type') + + self.show_bosh_fields(proxytype=='bosh') + self.proxyname_entry.set_editable(True) self.xml.get_widget('remove_proxy_button').set_sensitive(True) self.xml.get_widget('proxytype_combobox').set_sensitive(True) @@ -1227,11 +1271,16 @@ 'user')) proxypass_entry.set_text(gajim.config.get_per('proxies', proxy, 'pass')) - proxytype = gajim.config.get_per('proxies', proxy, 'type') - types = ['http', 'socks5'] + boshuri_entry.set_text(gajim.config.get_per('proxies', proxy, + 'bosh_uri')) + boshport_entry.set_text(unicode(gajim.config.get_per('proxies', proxy, + 'bosh_port'))) + types = ['http', 'socks5', 'bosh'] self.proxytype_combobox.set_active(types.index(proxytype)) - if gajim.config.get_per('proxies', proxy, 'user'): - useauth_checkbutton.set_active(True) + boshuseproxy_checkbutton.set_active( + gajim.config.get_per('proxies', proxy, 'bosh_useproxy')) + useauth_checkbutton.set_active( + gajim.config.get_per('proxies', proxy, 'useauth'))
def on_proxies_treeview_key_press_event(self, widget, event): if event.keyval == gtk.keysyms.Delete: @@ -1256,8 +1305,9 @@ model.set_value(iter_, 0, new_name)
def on_proxytype_combobox_changed(self, widget): - types = ['http', 'socks5'] + types = ['http', 'socks5', 'bosh'] type_ = self.proxytype_combobox.get_active() + self.show_bosh_fields(types[type_]=='bosh') proxy = self.proxyname_entry.get_text().decode('utf-8') gajim.config.set_per('proxies', proxy, 'type', types[type_])
@@ -1276,6 +1326,16 @@ proxy = self.proxyname_entry.get_text().decode('utf-8') gajim.config.set_per('proxies', proxy, 'user', value)
+ def on_boshuri_entry_changed(self, widget): + value = widget.get_text().decode('utf-8') + proxy = self.proxyname_entry.get_text().decode('utf-8') + gajim.config.set_per('proxies', proxy, 'bosh_uri', value) + + def on_boshport_entry_changed(self, widget): + value = widget.get_text().decode('utf-8') + proxy = self.proxyname_entry.get_text().decode('utf-8') + gajim.config.set_per('proxies', proxy, 'bosh_port', value) + def on_proxypass_entry_changed(self, widget): value = widget.get_text().decode('utf-8') proxy = self.proxyname_entry.get_text().decode('utf-8')
Modified: branches/osx_newbuildsys/src/dialogs.py =================================================================== --- branches/osx_newbuildsys/src/dialogs.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/dialogs.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -2352,6 +2352,13 @@ for to_whom_jid in sender_list: if to_whom_jid in self.completion_dict: to_whom_jid = self.completion_dict[to_whom_jid].jid + try: + to_whom_jid = helpers.parse_jid(to_whom_jid) + except helpers.InvalidFormat: + ErrorDialog(_('Invalid Jabber ID'), + _('It is not possible to send a message to %s, this JID is not ' + 'valid.') % to_whom_jid) + return
subject = self.subject_entry.get_text().decode('utf-8') begin, end = self.message_tv_buffer.get_bounds() @@ -3168,7 +3175,8 @@ active = widget.get_active() if active == 1: # user selected 'choose sound' def on_ok(widget, path_to_snd_file): - print path_to_snd_file + pass + #print path_to_snd_file
def on_cancel(widget): widget.set_active(0) # go back to No Sound @@ -3180,11 +3188,9 @@ conditions = ('online', 'chat', 'online_and_chat', 'away', 'xa', 'away_and_xa', 'dnd', 'xa_and_dnd', 'offline') active = self.condition_combobox.get_active() - print conditions[active]
active_iter = self.listen_sound_combobox.get_active_iter() listen_sound_model = self.listen_sound_combobox.get_model() - print listen_sound_model[active_iter][0]
class AdvancedNotificationsWindow: events_list = ['message_received', 'contact_connected',
Modified: branches/osx_newbuildsys/src/gajim.py =================================================================== --- branches/osx_newbuildsys/src/gajim.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/gajim.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -71,7 +71,8 @@ consoleloghandler = logging.StreamHandler() consoleloghandler.setLevel(1) consoleloghandler.setFormatter( -logging.Formatter('%(asctime)s %(name)s: %(levelname)s: %(message)s')) + logging.Formatter('%(asctime)s %(name)s: %(levelname)s: %(message)s') + ) log = logging.getLogger('gajim') log.setLevel(logging.WARNING) log.addHandler(consoleloghandler) @@ -266,7 +267,7 @@
from common.xmpp import idlequeue from common.zeroconf import connection_zeroconf -from common import nslookup +from common import resolver from common import proxy65_manager from common import socks5 from common import helpers @@ -625,7 +626,7 @@ ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
def handle_event_con_type(self, account, con_type): - # ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp' + # ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'plain' gajim.con_types[account] = con_type self.roster.draw_account(account)
@@ -849,20 +850,17 @@ # lost they'll be not decryptable (note that # this contradicts XEP-0201 - trying to get that # in the XEP, though) - # - # FIXME: This *REALLY* are TOO many leves of - # indentation! We even need to introduce - # a temp var here to make it somehow fit! + + # there won't be any sessions here if the contact terminated + # their sessions before going offline (which we do) for sess in conn.get_sessions(ji): if (ji+'/'+resource) != str(sess.jid): continue - ctrl = sess.control - if ctrl: - ctrl.no_autonegotiation = False + if sess.control: + sess.control.no_autonegotiation = False if sess.enable_encryption: sess.terminate_e2e() - conn.delete_session(jid, - sess.thread_id) + conn.delete_session(jid, sess.thread_id)
self.roster.chg_contact_status(contact1, array[1], status_message, account) @@ -3168,7 +3166,7 @@ # gajim.idlequeue.process() each foo miliseconds gajim.idlequeue = GlibIdleQueue() # resolve and keep current record of resolved hosts - gajim.resolver = nslookup.Resolver(gajim.idlequeue) + gajim.resolver = resolver.get_resolver(gajim.idlequeue) gajim.socks5queue = socks5.SocksQueue(gajim.idlequeue, self.handle_event_file_rcv_completed, self.handle_event_file_progress, @@ -3329,6 +3327,10 @@ gobject.timeout_add_seconds(gajim.config.get( 'check_idle_every_foo_seconds'), self.read_sleepy)
+ # when using libasyncns we need to process resolver in regular intervals + if resolver.USE_LIBASYNCNS: + gobject.timeout_add(200, gajim.resolver.process) + def remote_init(): if gajim.config.get('remote_control'): try:
Modified: branches/osx_newbuildsys/src/groupchat_control.py =================================================================== --- branches/osx_newbuildsys/src/groupchat_control.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/groupchat_control.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -928,31 +928,18 @@ special_words = [word.lower() for word in special_words if word] text = text.lower()
- text_splitted = text.split() - for word in text_splitted: # get each word of the text - for special_word in special_words: - if word.startswith(special_word): - # get char after the word that highlight us - char_position = len(special_word) - refer_to_nick_char = \ - word[char_position:char_position+1] - refer_to_nick_char = str(refer_to_nick_char) - if (refer_to_nick_char != ''): - if refer_to_nick_char.isalpha(): - # This is A->Z, a->z or 0-9, we can be sure our nick is the - # beginning of a real word, do not highlight for this one. - break - else: - return True - else: # Special word == word, no char after in word - return True for special_word in special_words: - if special_word.find(' ') > -1: - # There is a space in this special word, do a global search - # without splitting by words as previously - # We don't search this in all cases so we don't loose time - if text.find(special_word) > -1: + found_here = text.find(special_word) + while(found_here > -1): + end_here = found_here + len(special_word) + if (found_here == 0 or not text[found_here - 1].isalpha()) and \ + (end_here == len(text) or not text[end_here].isalpha()): + # It is beginning of text or char before is not alpha AND + # it is end of text or char after is not alpha return True + # continue searching + start = found_here + 1 + found_here = text.find(special_word, start) return False
def set_subject(self, subject):
Modified: branches/osx_newbuildsys/src/notify.py =================================================================== --- branches/osx_newbuildsys/src/notify.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/notify.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -550,12 +550,17 @@ if version >= [0, 3, 2]: hints['urgency'] = dbus.Byte(0) # Low Urgency hints['category'] = dbus.String(ntype) + # it seems notification-daemon doesn't like empty text + if self.text: + text = self.text + else: + text = ' ' self.notif.Notify( dbus.String(_('Gajim')), dbus.UInt32(0), # this notification does not replace other dbus.String(self.path_to_image), dbus.String(self.title), - dbus.String(self.text), + dbus.String(text), ( dbus.String('default'), dbus.String(self.event_type) ), hints, dbus.UInt32(timeout*1000),
Modified: branches/osx_newbuildsys/src/session.py =================================================================== --- branches/osx_newbuildsys/src/session.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/session.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -46,15 +46,14 @@
def detach_from_control(self): if self.control: - self.control.no_autonegotiation = False self.control.set_session(None)
def acknowledge_termination(self): self.detach_from_control() stanza_session.EncryptedStanzaSession.acknowledge_termination(self)
- def terminate(self): - stanza_session.EncryptedStanzaSession.terminate(self) + def terminate(self, send_termination = True): + stanza_session.EncryptedStanzaSession.terminate(self, send_termination) self.detach_from_control()
def get_chatstate(self, msg, msgtxt):
Modified: branches/osx_newbuildsys/src/systray.py =================================================================== --- branches/osx_newbuildsys/src/systray.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/src/systray.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -185,6 +185,8 @@ if connected_accounts < 1: item.set_sensitive(False)
+ connected_accounts_with_private_storage = 0 + item = gtk.SeparatorMenuItem() sub_menu.append(item)
@@ -200,58 +202,77 @@ single_message_menuitem.set_sensitive(iskey) join_gc_menuitem.set_sensitive(iskey)
- if connected_accounts >= 2: # 2 or more connections? make submenus + accounts_list = sorted(gajim.contacts.get_accounts()) + # items that get shown whether an account is zeroconf or not + if connected_accounts > 1: # 2 or more connections? make submenus account_menu_for_chat_with = gtk.Menu() chat_with_menuitem.set_submenu(account_menu_for_chat_with) self.popup_menus.append(account_menu_for_chat_with)
- account_menu_for_single_message = gtk.Menu() - single_message_menuitem.set_submenu( - account_menu_for_single_message) - self.popup_menus.append(account_menu_for_single_message) - - accounts_list = sorted(gajim.contacts.get_accounts()) for account in accounts_list: - if gajim.connections[account].is_zeroconf: - continue - if gajim.connections[account].connected > 1: + if gajim.account_is_connected(account): # for chat_with item = gtk.MenuItem(_('using account %s') % account) account_menu_for_chat_with.append(item) item.connect('activate', self.on_new_chat, account)
- # for single message - item = gtk.MenuItem(_('using account %s') % account) - item.connect('activate', - self.on_single_message_menuitem_activate, account) - account_menu_for_single_message.append(item) - - # join gc - gc_item = gtk.MenuItem(_('using account %s') % account, False) - gc_sub_menu.append(gc_item) - gc_menuitem_menu = gtk.Menu() - gajim.interface.roster.add_bookmarks_list(gc_menuitem_menu, - account) - gc_item.set_submenu(gc_menuitem_menu) - gc_sub_menu.show_all() - elif connected_accounts == 1: # one account # one account connected, no need to show 'as jid' for account in gajim.connections: if gajim.connections[account].connected > 1: + # for start chat self.new_chat_handler_id = chat_with_menuitem.connect( - 'activate', self.on_new_chat, account) + 'activate', self.on_new_chat, account) + break # No other connected account + + # menu items that don't apply to zeroconf connections + if connected_accounts == 1 or (connected_accounts == 2 and \ + gajim.zeroconf_is_connected()): + # only one 'real' (non-zeroconf) account is connected, don't need + # submenus + for account in gajim.connections: + if gajim.account_is_connected(account) and \ + not gajim.config.get_per('accounts', account, 'is_zeroconf'): + if gajim.connections[account].private_storage_supported: + connected_accounts_with_private_storage += 1 + # for single message single_message_menuitem.remove_submenu() self.single_message_handler_id = single_message_menuitem.\ connect('activate', self.on_single_message_menuitem_activate, account) - # join gc gajim.interface.roster.add_bookmarks_list(gc_sub_menu, account) - break # No other connected account + break # No other account connected + else: + # 2 or more 'real' accounts are connected, make submenus + account_menu_for_single_message = gtk.Menu() + single_message_menuitem.set_submenu( + account_menu_for_single_message) + self.popup_menus.append(account_menu_for_single_message)
+ for account in accounts_list: + if gajim.connections[account].is_zeroconf or \ + not gajim.account_is_connected(account): + continue + if gajim.connections[account].private_storage_supported: + connected_accounts_with_private_storage += 1 + # for single message + item = gtk.MenuItem(_('using account %s') % account) + item.connect('activate', + self.on_single_message_menuitem_activate, account) + account_menu_for_single_message.append(item) + + # join gc + gc_item = gtk.MenuItem(_('using account %s') % account, False) + gc_sub_menu.append(gc_item) + gc_menuitem_menu = gtk.Menu() + gajim.interface.roster.add_bookmarks_list(gc_menuitem_menu, + account) + gc_item.set_submenu(gc_menuitem_menu) + gc_sub_menu.show_all() + newitem = gtk.SeparatorMenuItem() # separator gc_sub_menu.append(newitem) newitem = gtk.ImageMenuItem(_('_Manage Bookmarks...')) @@ -260,6 +281,8 @@ newitem.connect('activate', gajim.interface.roster.on_manage_bookmarks_menuitem_activate) gc_sub_menu.append(newitem) + if connected_accounts_with_private_storage == 0: + newitem.set_sensitive(False)
sounds_mute_menuitem.set_active(not gajim.config.get('sounds_on'))
Copied: branches/osx_newbuildsys/test/test_client_nb.py (from rev 10894, trunk/test/test_client_nb.py) =================================================================== --- branches/osx_newbuildsys/test/test_client_nb.py (rev 0) +++ branches/osx_newbuildsys/test/test_client_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,932 @@ +import unittest +from xmpp_mocks import * + +import sys, os.path + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +import client_nb + +''' +Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py) +It actually connects to a xmpp server so the connection values have to be +changed before running. +''' + + +xmpp_server_port = ('gajim.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +credentials = ['loginn', 'passwo', 'testresour'] +''' +[username, password, passphrase] +Script will autheticate itself with this credentials on above mentioned server. +''' + +class TestNonBlockingClient(unittest.TestCase): + ''' + Test Cases class for NonBlockingClient. + ''' + + def setUp(self): + ''' + IdleQueue thread is run and dummy connection is created. + ''' + + self.idlequeue_thread = IdleQueueThread() + self.connection = MockConnectionClass() + + self.idlequeue_thread.start() + + def tearDown(self): + ''' + IdleQueue thread is stopped. + ''' + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def open_stream(self, server_port): + ''' + Method opening the XMPP connection. It returns when stream:features + is received from server. + + :param server_port: tuple of (hostname, port) for where the client should + connect. + + ''' + self.client = client_nb.NonBlockingClient( + domain=server_port[0], + idlequeue=self.idlequeue_thread.iq, + ) + ''' + NonBlockingClient instance with parameters from global variables and with + callbacks from dummy connection. + ''' + + self.client.connect( + on_connect=lambda *args: self.connection.on_connect(True, *args), + on_connect_failure=lambda *args: self.connection.on_connect(False, *args), + ) + + print 'waiting for callback from client constructor' + self.connection.wait() + + # if on_connect was called, client has to be connected and vice versa + if self.connection.connect_succeeded: + self.assert_(self.client.get_connect_type()) + else: + self.assert_(not self.client.get_connect_type()) + + def client_auth(self, username, password, resource, sasl): + ''' + Method authenticating connected client with supplied credentials. Returns + when authentication is over. + :param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication + + :todo: to check and be more specific about when it returns (bind, session..) + ''' + self.client.auth(username, password, resource, sasl, + on_auth=self.connection.on_auth) + + print 'waiting for authentication...' + self.connection.wait() + + def do_disconnect(self): + ''' + Does disconnecting of connected client. Returns when TCP connection is closed. + ''' + #self.client.start_disconnect(None, on_disconnect=self.connection.set_event) + self.client.RegisterDisconnectHandler(self.connection.set_event) + self.client.disconnect() + + print 'waiting for disconnecting...' + self.connection.wait() + + def test_proper_connect_sasl(self): + ''' + The ideal testcase - client is connected, authenticated with SASL and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + + # if client is not connected, lets raise the AssertionError + self.assert_(self.client.get_connect_type()) + # (client.disconnect() is already called from NBClient._on_connected_failure + # so there's need to call it in this case + + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='sasl') + + self.do_disconnect() + + + def test_proper_connect_oldauth(self): + ''' + The ideal testcase - client is connected, authenticated with old auth and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='old_auth') + self.do_disconnect() + + def test_connect_to_nonexisting_host(self): + ''' + Connect to nonexisting host. DNS request for A records should return nothing. + ''' + self.open_stream(('fdsfsdf.fdsf.fss', 5222)) + print 'nonexthost: %s' % self.client.get_connect_type() + self.assert_(not self.client.get_connect_type()) + + def test_connect_to_wrong_port(self): + ''' + Connect to nonexisting host. DNS request for A records should return some IP + but there shouldn't be XMPP server running on specified port. + ''' + self.open_stream((xmpp_server_port[0], 31337)) + self.assert_(not self.client.get_connect_type()) + + def test_connect_with_wrong_creds(self): + ''' + Connecting with invalid password. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], "wrong pass", credentials[2], sasl=1) + self.assert_(self.connection.auth is None) + self.do_disconnect() + + + + + + +if __name__ == '__main__': + + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingClient) + #suite = unittest.TestSuite() + #suite.addTest(TestNonBlockingClient('test_proper_connect_oldauth')) + #suite.addTest(TestNonBlockingClient('test_connect_to_nonexisting_host')) + + unittest.TextTestRunner(verbosity=2).run(suite) + + + + + +import unittest +from xmpp_mocks import * + +import sys, os.path + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +import client_nb + +''' +Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py) +It actually connects to a xmpp server so the connection values have to be +changed before running. +''' + + +xmpp_server_port = ('xmpp.example.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +credentials = ['loginn', 'passwo', 'testresour'] +''' +[username, password, passphrase] +Script will autheticate itself with this credentials on above mentioned server. +''' + +class TestNonBlockingClient(unittest.TestCase): + ''' + Test Cases class for NonBlockingClient. + ''' + + def setUp(self): + ''' + IdleQueue thread is run and dummy connection is created. + ''' + + self.idlequeue_thread = IdleQueueThread() + self.connection = MockConnectionClass() + + self.idlequeue_thread.start() + + def tearDown(self): + ''' + IdleQueue thread is stopped. + ''' + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def open_stream(self, server_port): + ''' + Method opening the XMPP connection. It returns when stream:features + is received from server. + + :param server_port: tuple of (hostname, port) for where the client should + connect. + + ''' + self.client = client_nb.NonBlockingClient( + hostname=server_port[0], + port=server_port[1], + caller=self.connection, + idlequeue=self.idlequeue_thread.iq, + ) + ''' + NonBlockingClient instance with parameters from global variables and with + callbacks from dummy connection. + ''' + + self.client.connect( + on_connect=lambda *args: self.connection.on_connect(True, *args), + on_connect_failure=lambda *args: self.connection.on_connect(False, *args), + secure=False + ) + + print 'waiting for callback from client constructor' + self.connection.wait() + + # if on_connect was called, client has to be connected and vice versa + if self.connection.connect_succeeded: + self.assert_(self.client.get_connect_type()) + else: + self.assert_(not self.client.get_connect_type()) + + def client_auth(self, username, password, resource, sasl): + ''' + Method authenticating connected client with supplied credentials. Returns + when authentication is over. + :param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication + + :todo: to check and be more specific about when it returns (bind, session..) + ''' + self.client.auth(username, password, resource, sasl, + on_auth=self.connection.on_auth) + + print 'waiting for authentication...' + self.connection.wait() + + def do_disconnect(self): + ''' + Does disconnecting of connected client. Returns when TCP connection is closed. + ''' + #self.client.start_disconnect(None, on_disconnect=self.connection.set_event) + self.client.RegisterDisconnectHandler(self.connection.set_event) + self.client.disconnect() + + print 'waiting for disconnecting...' + self.connection.wait() + + def test_proper_connect_sasl(self): + ''' + The ideal testcase - client is connected, authenticated with SASL and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + + # if client is not connected, lets raise the AssertionError + self.assert_(self.client.get_connect_type()) + # (client.disconnect() is already called from NBClient._on_connected_failure + # so there's need to call it in this case + + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='sasl') + + self.do_disconnect() + + + def test_proper_connect_oldauth(self): + ''' + The ideal testcase - client is connected, authenticated with old auth and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='old_auth') + self.do_disconnect() + + def test_connect_to_nonexisting_host(self): + ''' + Connect to nonexisting host. DNS request for A records should return nothing. + ''' + self.open_stream(('fdsfsdf.fdsf.fss', 5222)) + print 'nonexthost: %s' % self.client.get_connect_type() + self.assert_(not self.client.get_connect_type()) + + def test_connect_to_wrong_port(self): + ''' + Connect to nonexisting host. DNS request for A records should return some IP + but there shouldn't be XMPP server running on specified port. + ''' + self.open_stream((xmpp_server_port[0], 31337)) + self.assert_(not self.client.get_connect_type()) + + def test_connect_with_wrong_creds(self): + ''' + Connecting with invalid password. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], "wrong pass", credentials[2], sasl=1) + self.assert_(self.connection.auth is None) + self.do_disconnect() + + + + + + +if __name__ == '__main__': + + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingClient) + #suite = unittest.TestSuite() + #suite.addTest(TestNonBlockingClient('test_proper_connect_oldauth')) + #suite.addTest(TestNonBlockingClient('test_connect_to_nonexisting_host')) + + unittest.TextTestRunner(verbosity=2).run(suite) + + + + + +import unittest +from xmpp_mocks import * + +import sys, os.path + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +import client_nb + +''' +Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py) +It actually connects to a xmpp server so the connection values have to be +changed before running. +''' + + +xmpp_server_port = ('xmpp.example.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +credentials = ['loginn', 'passwo', 'testresour'] +''' +[username, password, passphrase] +Script will autheticate itself with this credentials on above mentioned server. +''' + +class TestNonBlockingClient(unittest.TestCase): + ''' + Test Cases class for NonBlockingClient. + ''' + + def setUp(self): + ''' + IdleQueue thread is run and dummy connection is created. + ''' + + self.idlequeue_thread = IdleQueueThread() + self.connection = MockConnectionClass() + + self.idlequeue_thread.start() + + def tearDown(self): + ''' + IdleQueue thread is stopped. + ''' + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def open_stream(self, server_port): + ''' + Method opening the XMPP connection. It returns when stream:features + is received from server. + + :param server_port: tuple of (hostname, port) for where the client should + connect. + + ''' + self.client = client_nb.NonBlockingClient( + hostname=server_port[0], + port=server_port[1], + caller=self.connection, + idlequeue=self.idlequeue_thread.iq, + ) + ''' + NonBlockingClient instance with parameters from global variables and with + callbacks from dummy connection. + ''' + + self.client.connect( + on_connect=lambda *args: self.connection.on_connect(True, *args), + on_connect_failure=lambda *args: self.connection.on_connect(False, *args), + secure=False + ) + + print 'waiting for callback from client constructor' + self.connection.wait() + + # if on_connect was called, client has to be connected and vice versa + if self.connection.connect_succeeded: + self.assert_(self.client.get_connect_type()) + else: + self.assert_(not self.client.get_connect_type()) + + def client_auth(self, username, password, resource, sasl): + ''' + Method authenticating connected client with supplied credentials. Returns + when authentication is over. + :param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication + + :todo: to check and be more specific about when it returns (bind, session..) + ''' + self.client.auth(username, password, resource, sasl, + on_auth=self.connection.on_auth) + + print 'waiting for authentication...' + self.connection.wait() + + def do_disconnect(self): + ''' + Does disconnecting of connected client. Returns when TCP connection is closed. + ''' + #self.client.start_disconnect(None, on_disconnect=self.connection.set_event) + self.client.RegisterDisconnectHandler(self.connection.set_event) + self.client.disconnect() + + print 'waiting for disconnecting...' + self.connection.wait() + + def test_proper_connect_sasl(self): + ''' + The ideal testcase - client is connected, authenticated with SASL and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + + # if client is not connected, lets raise the AssertionError + self.assert_(self.client.get_connect_type()) + # (client.disconnect() is already called from NBClient._on_connected_failure + # so there's need to call it in this case + + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='sasl') + + self.do_disconnect() + + + def test_proper_connect_oldauth(self): + ''' + The ideal testcase - client is connected, authenticated with old auth and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='old_auth') + self.do_disconnect() + + def test_connect_to_nonexisting_host(self): + ''' + Connect to nonexisting host. DNS request for A records should return nothing. + ''' + self.open_stream(('fdsfsdf.fdsf.fss', 5222)) + print 'nonexthost: %s' % self.client.get_connect_type() + self.assert_(not self.client.get_connect_type()) + + def test_connect_to_wrong_port(self): + ''' + Connect to nonexisting host. DNS request for A records should return some IP + but there shouldn't be XMPP server running on specified port. + ''' + self.open_stream((xmpp_server_port[0], 31337)) + self.assert_(not self.client.get_connect_type()) + + def test_connect_with_wrong_creds(self): + ''' + Connecting with invalid password. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], "wrong pass", credentials[2], sasl=1) + self.assert_(self.connection.auth is None) + self.do_disconnect() + + + + + + +if __name__ == '__main__': + + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingClient) + #suite = unittest.TestSuite() + #suite.addTest(TestNonBlockingClient('test_proper_connect_oldauth')) + #suite.addTest(TestNonBlockingClient('test_connect_to_nonexisting_host')) + + unittest.TextTestRunner(verbosity=2).run(suite) + + + + + +import unittest +from xmpp_mocks import * + +import sys, os.path + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +import client_nb + +''' +Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py) +It actually connects to a xmpp server so the connection values have to be +changed before running. +''' + + +xmpp_server_port = ('xmpp.example.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +credentials = ['loginn', 'passwo', 'testresour'] +''' +[username, password, passphrase] +Script will autheticate itself with this credentials on above mentioned server. +''' + +class TestNonBlockingClient(unittest.TestCase): + ''' + Test Cases class for NonBlockingClient. + ''' + + def setUp(self): + ''' + IdleQueue thread is run and dummy connection is created. + ''' + + self.idlequeue_thread = IdleQueueThread() + self.connection = MockConnectionClass() + + self.idlequeue_thread.start() + + def tearDown(self): + ''' + IdleQueue thread is stopped. + ''' + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def open_stream(self, server_port): + ''' + Method opening the XMPP connection. It returns when stream:features + is received from server. + + :param server_port: tuple of (hostname, port) for where the client should + connect. + + ''' + self.client = client_nb.NonBlockingClient( + hostname=server_port[0], + port=server_port[1], + caller=self.connection, + idlequeue=self.idlequeue_thread.iq, + ) + ''' + NonBlockingClient instance with parameters from global variables and with + callbacks from dummy connection. + ''' + + self.client.connect( + on_connect=lambda *args: self.connection.on_connect(True, *args), + on_connect_failure=lambda *args: self.connection.on_connect(False, *args), + secure=False + ) + + print 'waiting for callback from client constructor' + self.connection.wait() + + # if on_connect was called, client has to be connected and vice versa + if self.connection.connect_succeeded: + self.assert_(self.client.get_connect_type()) + else: + self.assert_(not self.client.get_connect_type()) + + def client_auth(self, username, password, resource, sasl): + ''' + Method authenticating connected client with supplied credentials. Returns + when authentication is over. + :param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication + + :todo: to check and be more specific about when it returns (bind, session..) + ''' + self.client.auth(username, password, resource, sasl, + on_auth=self.connection.on_auth) + + print 'waiting for authentication...' + self.connection.wait() + + def do_disconnect(self): + ''' + Does disconnecting of connected client. Returns when TCP connection is closed. + ''' + #self.client.start_disconnect(None, on_disconnect=self.connection.set_event) + self.client.RegisterDisconnectHandler(self.connection.set_event) + self.client.disconnect() + + print 'waiting for disconnecting...' + self.connection.wait() + + def test_proper_connect_sasl(self): + ''' + The ideal testcase - client is connected, authenticated with SASL and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + + # if client is not connected, lets raise the AssertionError + self.assert_(self.client.get_connect_type()) + # (client.disconnect() is already called from NBClient._on_connected_failure + # so there's need to call it in this case + + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='sasl') + + self.do_disconnect() + + + def test_proper_connect_oldauth(self): + ''' + The ideal testcase - client is connected, authenticated with old auth and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='old_auth') + self.do_disconnect() + + def test_connect_to_nonexisting_host(self): + ''' + Connect to nonexisting host. DNS request for A records should return nothing. + ''' + self.open_stream(('fdsfsdf.fdsf.fss', 5222)) + print 'nonexthost: %s' % self.client.get_connect_type() + self.assert_(not self.client.get_connect_type()) + + def test_connect_to_wrong_port(self): + ''' + Connect to nonexisting host. DNS request for A records should return some IP + but there shouldn't be XMPP server running on specified port. + ''' + self.open_stream((xmpp_server_port[0], 31337)) + self.assert_(not self.client.get_connect_type()) + + def test_connect_with_wrong_creds(self): + ''' + Connecting with invalid password. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], "wrong pass", credentials[2], sasl=1) + self.assert_(self.connection.auth is None) + self.do_disconnect() + + + + + + +if __name__ == '__main__': + + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingClient) + #suite = unittest.TestSuite() + #suite.addTest(TestNonBlockingClient('test_proper_connect_oldauth')) + #suite.addTest(TestNonBlockingClient('test_connect_to_nonexisting_host')) + + unittest.TextTestRunner(verbosity=2).run(suite) + + + + + +import unittest +from xmpp_mocks import * + +import sys, os.path + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +import client_nb + +''' +Testing script for NonBlockingClient class (src/common/xmpp/client_nb.py) +It actually connects to a xmpp server so the connection values have to be +changed before running. +''' + + +xmpp_server_port = ('xmpp.example.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +credentials = ['loginn', 'passwo', 'testresour'] +''' +[username, password, passphrase] +Script will autheticate itself with this credentials on above mentioned server. +''' + +class TestNonBlockingClient(unittest.TestCase): + ''' + Test Cases class for NonBlockingClient. + ''' + + def setUp(self): + ''' + IdleQueue thread is run and dummy connection is created. + ''' + + self.idlequeue_thread = IdleQueueThread() + self.connection = MockConnectionClass() + + self.idlequeue_thread.start() + + def tearDown(self): + ''' + IdleQueue thread is stopped. + ''' + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def open_stream(self, server_port): + ''' + Method opening the XMPP connection. It returns when stream:features + is received from server. + + :param server_port: tuple of (hostname, port) for where the client should + connect. + + ''' + self.client = client_nb.NonBlockingClient( + hostname=server_port[0], + port=server_port[1], + caller=self.connection, + idlequeue=self.idlequeue_thread.iq, + ) + ''' + NonBlockingClient instance with parameters from global variables and with + callbacks from dummy connection. + ''' + + self.client.connect( + on_connect=lambda *args: self.connection.on_connect(True, *args), + on_connect_failure=lambda *args: self.connection.on_connect(False, *args), + secure=False + ) + + print 'waiting for callback from client constructor' + self.connection.wait() + + # if on_connect was called, client has to be connected and vice versa + if self.connection.connect_succeeded: + self.assert_(self.client.get_connect_type()) + else: + self.assert_(not self.client.get_connect_type()) + + def client_auth(self, username, password, resource, sasl): + ''' + Method authenticating connected client with supplied credentials. Returns + when authentication is over. + :param sasl: whether to use sasl (sasl=1) or old (sasl=0) authentication + + :todo: to check and be more specific about when it returns (bind, session..) + ''' + self.client.auth(username, password, resource, sasl, + on_auth=self.connection.on_auth) + + print 'waiting for authentication...' + self.connection.wait() + + def do_disconnect(self): + ''' + Does disconnecting of connected client. Returns when TCP connection is closed. + ''' + #self.client.start_disconnect(None, on_disconnect=self.connection.set_event) + self.client.RegisterDisconnectHandler(self.connection.set_event) + self.client.disconnect() + + print 'waiting for disconnecting...' + self.connection.wait() + + def test_proper_connect_sasl(self): + ''' + The ideal testcase - client is connected, authenticated with SASL and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + + # if client is not connected, lets raise the AssertionError + self.assert_(self.client.get_connect_type()) + # (client.disconnect() is already called from NBClient._on_connected_failure + # so there's need to call it in this case + + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=1) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='sasl') + + self.do_disconnect() + + + def test_proper_connect_oldauth(self): + ''' + The ideal testcase - client is connected, authenticated with old auth and + then disconnected. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], credentials[1], credentials[2], sasl=0) + self.assert_(self.connection.con) + self.assert_(self.connection.auth=='old_auth') + self.do_disconnect() + + def test_connect_to_nonexisting_host(self): + ''' + Connect to nonexisting host. DNS request for A records should return nothing. + ''' + self.open_stream(('fdsfsdf.fdsf.fss', 5222)) + print 'nonexthost: %s' % self.client.get_connect_type() + self.assert_(not self.client.get_connect_type()) + + def test_connect_to_wrong_port(self): + ''' + Connect to nonexisting host. DNS request for A records should return some IP + but there shouldn't be XMPP server running on specified port. + ''' + self.open_stream((xmpp_server_port[0], 31337)) + self.assert_(not self.client.get_connect_type()) + + def test_connect_with_wrong_creds(self): + ''' + Connecting with invalid password. + ''' + self.open_stream(xmpp_server_port) + self.assert_(self.client.get_connect_type()) + self.client_auth(credentials[0], "wrong pass", credentials[2], sasl=1) + self.assert_(self.connection.auth is None) + self.do_disconnect() + + + + + + +if __name__ == '__main__': + + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingClient) + #suite = unittest.TestSuite() + #suite.addTest(TestNonBlockingClient('test_proper_connect_oldauth')) + #suite.addTest(TestNonBlockingClient('test_connect_to_nonexisting_host')) + + unittest.TextTestRunner(verbosity=2).run(suite) + + + + +
Modified: branches/osx_newbuildsys/test/test_dispatcher_nb.py =================================================================== --- branches/osx_newbuildsys/test/test_dispatcher_nb.py 2008-12-22 22:59:33 UTC (rev 10894) +++ branches/osx_newbuildsys/test/test_dispatcher_nb.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -7,18 +7,18 @@ from mock import Mock
from common.xmpp import dispatcher_nb -from common.xmpp import auth +from common.xmpp import auth_nb
class TestDispatcherNB(unittest.TestCase): def test_unbound_namespace_prefix(self): '''tests our handling of a message with an unbound namespace prefix''' - d = dispatcher_nb.Dispatcher() + d = dispatcher_nb.XMPPDispatcher()
conn = Mock()
owner = Mock() owner._caller = Mock() - owner.defaultNamespace = auth.NS_CLIENT + owner.defaultNamespace = auth_nb.NS_CLIENT owner.debug_flags = [] owner.Connection = conn owner._component = False
Copied: branches/osx_newbuildsys/test/test_nonblockingtcp.py (from rev 10894, trunk/test/test_nonblockingtcp.py) =================================================================== --- branches/osx_newbuildsys/test/test_nonblockingtcp.py (rev 0) +++ branches/osx_newbuildsys/test/test_nonblockingtcp.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,81 @@ +''' +Unit test for NonBlockingTCP tranport. +''' + +import unittest +from xmpp_mocks import * + +import threading, sys, os.path, time + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') +sys.path.append(gajim_root + '/src/common') + +import transports_nb +from client import * + +xmpp_server = ('gajim.org',5222) +''' +2-tuple - (XMPP server hostname, c2s port) +Script will connect to the machine. +''' + +import socket +ips = socket.getaddrinfo(xmpp_server[0], xmpp_server[1], socket.AF_UNSPEC,socket.SOCK_STREAM) + +# change xmpp_server on real values +ip = ips[0] + +class MockClient(IdleMock): + def __init__(self, idlequeue): + self.idlequeue=idlequeue + IdleMock.__init__(self) + + def do_connect(self): + self.socket=transports_nb.NonBlockingTCP( + lambda(event_type, data): sys.stdout.write('raising event %s: %s' % ( + event_type, data)), lambda: self.on_success(mode='SocketDisconnect'), + self.idlequeue, False, None) + + self.socket.PlugIn(self) + + self.socket.connect(conn_5tuple=ip, + on_connect=lambda: self.on_success(mode='TCPconnect'), + on_connect_failure=self.on_failure) + self.wait() + + def do_disconnect(self): + self.socket.disconnect() + self.wait() + + def on_failure(self, data): + print 'Error: %s' % data + self.set_event() + + def on_success(self, mode, data=None): + if mode == "TCPconnect": + pass + if mode == "SocketDisconnect": + pass + self.set_event() + +class TestNonBlockingTCP(unittest.TestCase): + def setUp(self): + self.idlequeue_thread = IdleQueueThread() + self.idlequeue_thread.start() + self.client = MockClient(idlequeue=self.idlequeue_thread.iq) + + def tearDown(self): + self.idlequeue_thread.stop_thread() + self.idlequeue_thread.join() + + def testSth(self): + self.client.do_connect() + self.assert_(self.client.socket.state == 'CONNECTED') + self.client.do_disconnect() + self.assert_(self.client.socket.state == 'DISCONNECTED') + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TestNonBlockingTCP) + unittest.TextTestRunner(verbosity=2).run(suite)
Copied: branches/osx_newbuildsys/test/test_resolver.py (from rev 10894, trunk/test/test_resolver.py) =================================================================== --- branches/osx_newbuildsys/test/test_resolver.py (rev 0) +++ branches/osx_newbuildsys/test/test_resolver.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,95 @@ +import unittest + +import time + +import lib +lib.setup_env() + +from common import resolver +from gajim import GlibIdleQueue + +from mock import Mock, expectParams +from mocks import * + +import gtk + + +GMAIL_SRV_NAME = '_xmpp-client._tcp.gmail.com' +NONSENSE_NAME = 'sfsdfsdfsdf.sdfs.fsd' +JABBERCZ_TXT_NAME = '_xmppconnect.jabber.cz' +JABBERCZ_SRV_NAME = '_xmpp-client._tcp.jabber.cz' + +TEST_LIST = [(GMAIL_SRV_NAME, 'srv', True), + (NONSENSE_NAME, 'srv', False), + (JABBERCZ_SRV_NAME, 'srv', True)] + +class TestResolver(unittest.TestCase): + def setUp(self): + self.iq = GlibIdleQueue() + self.reset() + self.resolver = None + + def reset(self): + self.flag = False + self.expect_results = False + self.nslookup = False + self.resolver = None + + def testLibAsyncNSResolver(self): + self.reset() + if not resolver.USE_LIBASYNCNS: + print 'testLibAsyncResolver: libasyncns-python not installed' + return + self.resolver = resolver.LibAsyncNSResolver() + + for name, type, expect_results in TEST_LIST: + self.expect_results = expect_results + self.runLANSR(name, type) + self.flag = False + + def runLANSR(self, name, type): + self.resolver.resolve( + host = name, + type = type, + on_ready = self.myonready) + while not self.flag: + time.sleep(1) + self.resolver.process() + + + def myonready(self, name, result_set): + print 'on_ready called ...' + print 'hostname: %s' % name + print 'result set: %s' % result_set + print 'res.resolved_hosts: %s' % self.resolver.resolved_hosts + if self.expect_results: + self.assert_(len(result_set) > 0) + else: + self.assert_(result_set == []) + self.flag = True + if self.nslookup: self._testNSLR() + + + def testNSLookupResolver(self): + self.reset() + self.nslookup = True + self.resolver = resolver.NSLookupResolver(self.iq) + self.test_list = TEST_LIST + self._testNSLR() + try: + gtk.main() + except KeyboardInterrupt: + print 'KeyboardInterrupt caught' + + def _testNSLR(self): + if self.test_list == []: + gtk.main_quit() + return + name, type, self.expect_results = self.test_list.pop() + self.resolver.resolve( + host = name, + type = type, + on_ready = self.myonready) + +if __name__ == '__main__': + unittest.main()
Copied: branches/osx_newbuildsys/test/xmpp_mocks.py (from rev 10894, trunk/test/xmpp_mocks.py) =================================================================== --- branches/osx_newbuildsys/test/xmpp_mocks.py (rev 0) +++ branches/osx_newbuildsys/test/xmpp_mocks.py 2008-12-23 23:42:49 UTC (rev 10895) @@ -0,0 +1,123 @@ +''' +Module with dummy classes for unit testing of xmpp code (src/common/xmpp/*). +''' + +import threading, time, os.path, sys + +import lib +lib.setup_env() + +from mock import Mock + +gajim_root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..') + +sys.path.append(gajim_root + '/src/common/xmpp') + +''' +Module with classes usable for XMPP related testing. +''' + +import idlequeue +from client import PlugIn + +idlequeue_interval = 0.2 +''' +IdleQueue polling interval. 200ms is used in Gajim as default +''' + + +class IdleQueueThread(threading.Thread): + ''' + Thread for regular processing of idlequeue. + ''' + def __init__(self): + self.iq = idlequeue.IdleQueue() + self.stop = threading.Event() + ''' + Event used to stopping the thread main loop. + ''' + + self.stop.clear() + threading.Thread.__init__(self) + + def run(self): + while not self.stop.isSet(): + self.iq.process() + time.sleep(idlequeue_interval) + + def stop_thread(self): + self.stop.set() + + + + +class IdleMock: + ''' + Serves as template for testing objects that are normally controlled by GUI. + Allows to wait for asynchronous callbacks with wait() method. + ''' + def __init__(self): + self.event = threading.Event() + ''' + Event is used for waiting on callbacks. + ''' + self.event.clear() + + + def wait(self): + ''' + Waiting until some callback sets the event and clearing the event subsequently. + ''' + self.event.wait() + self.event.clear() + + def set_event(self): + self.event.set() + + +class MockConnectionClass(IdleMock,Mock): + ''' + Class simulating Connection class from src/common/connection.py + + It is derived from Mock in order to avoid defining all methods + from real Connection that are called from NBClient or Dispatcher + ( _event_dispatcher for example) + ''' + + def __init__(self, *args): + self.connect_succeeded = True + IdleMock.__init__(self) + Mock.__init__(self, *args) + + def on_connect(self, success, *args): + ''' + Method called after connecting - after receiving stream:features + from server (NOT after TLS stream restart) or connect failure + ''' + + print 'on_connect - args:' + print ' success - %s' % success + for i in args: + print ' %s' % i + self.connect_succeeded = success + self.set_event() + + def on_auth(self, con, auth): + ''' + Method called after authentication is done regardless on the result. + + :Parameters: + con : NonBlockingClient + reference to authenticated object + auth : string + type of authetication in case of success ('old_auth', 'sasl') or + None in case of auth failure + ''' + + #print 'on_auth - args:' + #print ' con: %s' % con + #print ' auth: %s' % auth + self.auth_connection = con + self.auth = auth + self.set_event() +