merge from trunk
This commit is contained in:
commit
0c36f50196
118 changed files with 7893 additions and 6967 deletions
|
@ -1,4 +1,4 @@
|
|||
Gajim 0.15 (XX XX 2011)
|
||||
Gajim 0.15 (18 March 2012)
|
||||
|
||||
* Plugin system
|
||||
* Whiteboard (via a plugin)
|
||||
|
@ -9,6 +9,7 @@ Gajim 0.15 (XX XX 2011)
|
|||
* Roster filtrering
|
||||
* UPower support
|
||||
* GPG support for windows
|
||||
* Spell checking support for windows
|
||||
|
||||
Gajim 0.14.4 (22 July 2011)
|
||||
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
<h2>Runtime Requirements</h2>
|
||||
<ul>
|
||||
<li>python2.5 or higher</li>
|
||||
<li>pygtk2.16 or higher</li>
|
||||
<li>pygtk2.22 or higher</li>
|
||||
</ul>
|
||||
|
||||
<h2>Optional Runtime Requirements</h2>
|
||||
<ul>
|
||||
<li><a href="http://pyopenssl.sourceforge.net/">PyOpenSSL</a> (python-pyopenssl package in Debian) (>=0.9) for <em>secure</em> SSL/TLS. Python's default SSL is insecure, so this package is highly recommended!</li>
|
||||
<li><a href="http://pyopenssl.sourceforge.net/">PyOpenSSL</a> (python-pyopenssl package in Debian) (>=0.12) for <em>secure</em> SSL/TLS. Python's default SSL is insecure, so this package is highly recommended!</li>
|
||||
<li>python-pyasn1 to check SSL/TLS certificate</li>
|
||||
<li>python-crypto to enable End to end encryption</li>
|
||||
<li>For idle module, libxss library</li>
|
||||
<li>For zeroconf (bonjour), the "enable link-local messaging" checkbox, you need dbus-glib, python-avahi</li>
|
||||
|
@ -111,7 +112,7 @@ Wiki can be found at <a href="http://trac.gajim.org/wiki">http://trac.gajim.org/
|
|||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
(C) 2003-2011<br/>
|
||||
(C) 2003-2012<br/>
|
||||
The Gajim Team<br/>
|
||||
http://gajim.org<br/>
|
||||
<br/>
|
||||
|
|
10
autogen.sh
10
autogen.sh
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
gajimversion="0.15-beta2"
|
||||
gajimversion="0.15"
|
||||
if [ -d ".hg" ]; then
|
||||
node=$(hg tip --template "{node}")
|
||||
hgversion="-${node:0:12}"
|
||||
|
@ -20,8 +20,12 @@
|
|||
&& for p in `ls data/gui/*.ui`; do echo "[type: gettext/glade]$p" >> \
|
||||
po/POTFILES.in; done \
|
||||
&& ls -1 data/gajim.desktop.in.in \
|
||||
src/*py src/common/*py src/common/zeroconf/*.py src/plugins/*.py| grep -v ipython_view.py >> \
|
||||
po/POTFILES.in || exit 1
|
||||
src/*.py src/common/*.py src/command_system/implementation/*.py src/common/zeroconf/*.py src/plugins/*.py | grep -v ipython_view.py >> \
|
||||
po/POTFILES.in \
|
||||
&& echo -e "data/gajim.desktop.in\nsrc/ipython_view.py" > po/POTFILES.skip || exit 1
|
||||
if [ $(find plugins/ -name '*.py' | wc -l) -gt 0 ];then
|
||||
ls -1 plugins/*/*.py plugins/*/*.ui >> po/POTFILES.skip
|
||||
fi
|
||||
if test -z `which pkg-config 2>/dev/null`;then
|
||||
echo "***Error: pkg-config not found***"
|
||||
echo "See README.html for build requirements."
|
||||
|
|
|
@ -47,12 +47,12 @@ AC_ARG_ENABLE(site-packages,
|
|||
instead of DATADIR/gajim/src.])]
|
||||
,
|
||||
AC_SUBST([gajim_srcdir], [\${pkgpythondir}])
|
||||
AC_SUBST([gajim_pluginsdir], [\${pkgpythondir}])
|
||||
,
|
||||
AC_SUBST([gajim_srcdir], [\${datadir}/\${PACKAGE}/src])
|
||||
AC_SUBST([gajim_pluginsdir], [\${datadir}/\${PACKAGE}/plugins])
|
||||
)
|
||||
|
||||
AC_SUBST([gajim_pluginsdir], [\${datadir}/\${PACKAGE}/plugins])
|
||||
|
||||
AS_AC_EXPAND(GAJIM_SRCDIR, "${gajim_srcdir}")
|
||||
AS_AC_EXPAND(PKGDATADIR, "${datadir}/${PACKAGE}")
|
||||
AS_AC_EXPAND(DOCDIR, "${docdir}")
|
||||
|
|
|
@ -308,7 +308,7 @@
|
|||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="synchronise_contacts_button1">
|
||||
<property name="label" translatable="yes">Synchronise contacts</property>
|
||||
<property name="label" translatable="yes">Synchronize contacts</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.16"/>
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="advanced_configuration_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="title" translatable="yes">Advanced Configuration Editor</property>
|
||||
<property name="role">ace</property>
|
||||
<property name="default_width">650</property>
|
||||
<property name="default_height">540</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<signal name="destroy" handler="on_advanced_configuration_window_destroy"/>
|
||||
<signal name="destroy" handler="on_advanced_configuration_window_destroy" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox70">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table26">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">2</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">12</property>
|
||||
|
@ -26,6 +27,7 @@
|
|||
<child>
|
||||
<object class="GtkLabel" id="label248">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">Filter:</property>
|
||||
</object>
|
||||
|
@ -38,7 +40,7 @@
|
|||
<object class="GtkEntry" id="advanced_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="changed" handler="on_advanced_entry_changed"/>
|
||||
<signal name="changed" handler="on_advanced_entry_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
|
@ -51,14 +53,16 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="advanced_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<signal name="row_activated" handler="on_advanced_treeview_row_activated"/>
|
||||
<signal name="row-activated" handler="on_advanced_treeview_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -71,23 +75,28 @@
|
|||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame36">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">3</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment90">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="advanced_desc_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
|
@ -97,6 +106,7 @@
|
|||
<child type="label">
|
||||
<object class="GtkLabel" id="label357">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Description</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
|
@ -104,11 +114,13 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="restart_label">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>NOTE:</b> You should restart Gajim for some settings to take effect</property>
|
||||
<property name="use_markup">True</property>
|
||||
|
@ -123,27 +135,50 @@
|
|||
<child>
|
||||
<object class="GtkHButtonBox" id="hbuttonbox18">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="reset_button">
|
||||
<property name="label" translatable="yes">_Reset to default</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">image1</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_reset_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="advanced_close_button">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_advanced_close_button_clicked"/>
|
||||
<signal name="clicked" handler="on_advanced_close_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
|
@ -151,4 +186,9 @@
|
|||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-undo</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<object class="GtkLabel" id="banner_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">label</property>
|
||||
<property name="label">label</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="selectable">True</property>
|
||||
<signal name="populate_popup" handler="on_banner_label_populate_popup"/>
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<object class="GtkLabel" id="banner_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes">label</property>
|
||||
<property name="label">label</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
|
|
|
@ -202,7 +202,7 @@
|
|||
<child>
|
||||
<object class="GtkCheckMenuItem" id="show_transports_menuitem">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Show T_rans_ports</property>
|
||||
<property name="label" translatable="yes">Show T_ransports</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_show_transports_menuitem_activate"/>
|
||||
</object>
|
||||
|
|
BIN
data/sounds/attention.wav
Normal file
BIN
data/sounds/attention.wav
Normal file
Binary file not shown.
2
debian/README.Debian
vendored
2
debian/README.Debian
vendored
|
@ -1,7 +1,7 @@
|
|||
gajim for Debian
|
||||
----------------
|
||||
|
||||
For video chat support, you have to install python-farsight.
|
||||
For video chat support, you have to install python-farstream.
|
||||
|
||||
-- Yann Le Boulanger <asterix@lagaule.org>, Mon, 20 Jun 2005 12:02:31 +0200
|
||||
-- Julien Valroff <julien@debian.org> Sat, 07 May 2011 13:50:27 +0200
|
||||
|
|
4
debian/changelog
vendored
4
debian/changelog
vendored
|
@ -1,11 +1,11 @@
|
|||
gajim (0.15~alpha1-1) unstable; urgency=low
|
||||
gajim (0.15-1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
* remove 00_debian-copying.diff because upstream doesn't install it anymore
|
||||
* remove 01_configure-ac.diff because upstream changed configure dependencies
|
||||
* remove python-gnupginterface from recommands list, it's no more used
|
||||
|
||||
-- Yann Leboulanger <asterix@lagaule.org> Fri, 26 Aug 2011 12:13:51 +0200
|
||||
-- Yann Leboulanger <asterix@lagaule.org> Sat, 18 Mar 2012 10:32:38 +0100
|
||||
|
||||
gajim (0.14.4-1) unstable; urgency=low
|
||||
|
||||
|
|
8
debian/control
vendored
8
debian/control
vendored
|
@ -3,16 +3,16 @@ Section: net
|
|||
Priority: optional
|
||||
Maintainer: Yann Leboulanger <asterix@lagaule.org>
|
||||
Build-Depends: debhelper (>= 7.0.50~), python (>= 2.6.6-3~), gettext (>= 0.17-4), intltool (>= 0.40.1), imagemagick, libglib2.0-dev
|
||||
Standards-Version: 3.9.2
|
||||
Standards-Version: 3.9.3
|
||||
Homepage: http://www.gajim.org
|
||||
Vcs-Hg: http://hg.gajim.org/gajim/
|
||||
Vcs-Browser: http://hg.gajim.org/gajim/file
|
||||
|
||||
Package: gajim
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, ${python:Depends}, python-gtk2 (>= 2.16.0), dnsutils
|
||||
Recommends: dbus, python-dbus, notification-daemon, python-openssl (>= 0.9), python-crypto
|
||||
Suggests: python-gconf, python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnomekeyring, gnome-keyring, python-kerberos (>= 1.1), texlive-latex-base, dvipng, python-farsight, gstreamer0.10-plugins-ugly
|
||||
Depends: ${misc:Depends}, ${python:Depends}, python-gtk2 (>= 2.22.0), dnsutils
|
||||
Recommends: dbus, python-dbus, notification-daemon, python-openssl (>= 0.12), python-crypto, python-pyasn1
|
||||
Suggests: python-gconf, python-gnome2, nautilus-sendto, avahi-daemon, python-avahi, network-manager, libgtkspell0, aspell-en, python-gnomekeyring, gnome-keyring, python-kerberos (>= 1.1), texlive-latex-base, dvipng, python-farstream, gstreamer0.10-plugins-ugly, python-pycurl, python-gupnp-igd
|
||||
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,
|
||||
|
|
2
debian/copyright
vendored
2
debian/copyright
vendored
|
@ -9,7 +9,7 @@ Upstream Authors:
|
|||
- Yann Leboulanger <asterix@lagaule.org>
|
||||
|
||||
|
||||
Copyright: (c) 2003-2011 Gajim Team
|
||||
Copyright: (c) 2003-2012 Gajim Team
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
138
gajim.nsi
138
gajim.nsi
|
@ -224,18 +224,8 @@ Section "Gtk+ 2" SecGtk
|
|||
SetOutPath "$INSTDIR\bin\gtk"
|
||||
File /r "bin\gtk\bin"
|
||||
File /r "bin\gtk\etc"
|
||||
SetOutPath "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0"
|
||||
File /r "bin\gtk\lib\gtk-2.0\2.10.0\loaders"
|
||||
SetOutPath "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines"
|
||||
File "bin\gtk\lib\gtk-2.0\2.10.0\engines\libclearlooks.dll"
|
||||
File "bin\gtk\lib\gtk-2.0\2.10.0\engines\libpixmap.dll"
|
||||
File "bin\gtk\lib\gtk-2.0\2.10.0\engines\libsvg.dll"
|
||||
SetOutPath "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0"
|
||||
SetOutPath "$INSTDIR\bin\gtk\lib"
|
||||
File "bin\gtk\lib\charset.alias"
|
||||
SetOutPath "$INSTDIR\bin\gtk\share"
|
||||
File /r "bin\gtk\share\gtkthemeselector"
|
||||
File /r "bin\gtk\share\xml"
|
||||
File /r "bin\gtk\lib"
|
||||
File /r "bin\gtk\share"
|
||||
SectionEnd
|
||||
|
||||
Section "Plugins" SecPlugins
|
||||
|
@ -527,128 +517,8 @@ SectionEnd
|
|||
Section "Uninstall"
|
||||
RMDir /r "$INSTDIR\bin\gtk\bin"
|
||||
RMDir /r "$INSTDIR\bin\gtk\etc"
|
||||
RMDir /r "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\loaders"
|
||||
Delete "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines\libclearlooks.dll"
|
||||
Delete "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines\libpixmap.dll"
|
||||
Delete "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines\libsvg.dll"
|
||||
Delete "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines\libwimp.dll"
|
||||
RMDir "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0\engines"
|
||||
RMDir "$INSTDIR\bin\gtk\lib\gtk-2.0\2.10.0"
|
||||
RMDir "$INSTDIR\bin\gtk\lib\gtk-2.0"
|
||||
Delete "$INSTDIR\bin\gtk\lib\charset.alias"
|
||||
RMDir "$INSTDIR\bin\gtk\lib"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\de"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\en_GB"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\es"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\fr"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\it"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ru"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\af"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\am"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ang"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ar"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\as"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\az"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\az_IR"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\be"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\be@latin"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\bg"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\bn"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\bn_IN"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\br"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\bs"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ca"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ca@valencia"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\cs"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\cy"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\da"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\dz"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\el"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\en_CA"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\eo"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\et"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\eu"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\fa"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\fi"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ga"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\gl"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\gu"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\he"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\hi"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\hr"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\hu"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\hy"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ia"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\id"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\io"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\is"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ja"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ka"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\kn"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ko"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ku"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\li"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\lt"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\lv"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mai"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mg"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mi"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mk"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ml"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mn"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\mr"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ms"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\nb"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ne"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\nl"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\nn"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\nso"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\oc"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\or"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\pa"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\pl"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ps"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\pt"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\pt_BR"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ro"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\rw"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\si"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sk"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sl"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sq"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sr"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sr@ije"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sr@latin"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\sv"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ta"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\te"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\th"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\tk"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\tl"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\tr"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\tt"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ug"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\uk"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\ur"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\uz"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\uz@cyrillic"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\vi"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\wa"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\xh"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\yi"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\zh_CN"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\zh_HK"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\locale\zh_TW"
|
||||
RMDir "$INSTDIR\bin\gtk\share\locale"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\themes\Clearlooks"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\themes\Default"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\themes\Glossy"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\themes\Glossy-js"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\themes\MS-Windows"
|
||||
RMDir "$INSTDIR\bin\gtk\share\themes"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\gtkthemeselector"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share\xml"
|
||||
RMDir "$INSTDIR\bin\gtk\share"
|
||||
RMDir /r "$INSTDIR\bin\gtk\lib"
|
||||
RMDir /r "$INSTDIR\bin\gtk\share"
|
||||
RMDir "$INSTDIR\bin\gtk"
|
||||
Delete "$INSTDIR\bin\_bsddb.pyd"
|
||||
Delete "$INSTDIR\bin\_ctypes.pyd"
|
||||
|
|
|
@ -501,7 +501,7 @@ if dbus_support.supported:
|
|||
for node in path:
|
||||
key += node + '#'
|
||||
key += name
|
||||
prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
|
||||
prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value)
|
||||
gajim.config.foreach(get_prefs)
|
||||
return prefs_dict
|
||||
|
||||
|
|
|
@ -1,772 +0,0 @@
|
|||
"""
|
||||
A python version of the main functions to use Snarl
|
||||
(http://www.fullphat.net/snarl)
|
||||
|
||||
Version 1.0
|
||||
|
||||
This module can be used in two ways. One is the normal way
|
||||
the other snarl interfaces work. This means you can call snShowMessage
|
||||
and get an ID back for manipulations.
|
||||
|
||||
The other way is there is a class this module exposes called SnarlMessage.
|
||||
This allows you to keep track of the message as a python object. If you
|
||||
use the send without specifying False as the argument it will set the ID
|
||||
to what the return of the last SendMessage was. This is of course only
|
||||
useful for the SHOW message.
|
||||
|
||||
Requires one of:
|
||||
pywin32 extensions from http://pywin32.sourceforge.net
|
||||
ctypes (included in Python 2.5, downloadable for earlier versions)
|
||||
|
||||
Creator: Sam Listopad II (samlii@users.sourceforge.net)
|
||||
|
||||
Copyright 2006-2008 Samuel Listopad II
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
use this file except in compliance with the License. You may obtain a copy
|
||||
of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
|
||||
by applicable law or agreed to in writing, software distributed under the
|
||||
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
OF ANY KIND, either express or implied. See the License for the specific
|
||||
language governing permissions and limitations under the License.
|
||||
"""
|
||||
|
||||
import array, struct
|
||||
|
||||
def LOWORD(dword):
|
||||
"""Return the low WORD of the passed in integer"""
|
||||
return dword & 0x0000ffff
|
||||
#get the hi word
|
||||
def HIWORD(dword):
|
||||
"""Return the high WORD of the passed in integer"""
|
||||
return dword >> 16
|
||||
|
||||
class Win32FuncException(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class Win32Funcs:
|
||||
"""Just a little class to hide the details of finding and using the
|
||||
correct win32 functions. The functions may throw a UnicodeEncodeError if
|
||||
there is not a unicode version and it is sent a unicode string that cannot
|
||||
be converted to ASCII."""
|
||||
WM_USER = 0x400
|
||||
WM_COPYDATA = 0x4a
|
||||
#Type of String the functions are expecting.
|
||||
#Used like function(myWin32Funcs.strType(param)).
|
||||
__strType = str
|
||||
#FindWindow function to use
|
||||
__FindWindow = None
|
||||
#FindWindow function to use
|
||||
__FindWindowEx = None
|
||||
#SendMessage function to use
|
||||
__SendMessage = None
|
||||
#SendMessageTimeout function to use
|
||||
__SendMessageTimeout = None
|
||||
#IsWindow function to use
|
||||
__IsWindow = None
|
||||
#RegisterWindowMessage to use
|
||||
__RegisterWindowMessage = None
|
||||
#GetWindowText to use
|
||||
__GetWindowText = None
|
||||
|
||||
def FindWindow(self, lpClassName, lpWindowName):
|
||||
"""Wraps the windows API call of FindWindow"""
|
||||
if lpClassName is not None:
|
||||
lpClassName = self.__strType(lpClassName)
|
||||
if lpWindowName is not None:
|
||||
lpWindowName = self.__strType(lpWindowName)
|
||||
return self.__FindWindow(lpClassName, lpWindowName)
|
||||
|
||||
def FindWindowEx(self, hwndParent, hwndChildAfter, lpClassName, lpWindowName):
|
||||
"""Wraps the windows API call of FindWindow"""
|
||||
if lpClassName is not None:
|
||||
lpClassName = self.__strType(lpClassName)
|
||||
if lpWindowName is not None:
|
||||
lpWindowName = self.__strType(lpWindowName)
|
||||
return self.__FindWindowEx(hwndParent, hwndChildAfter, lpClassName, lpWindowName)
|
||||
|
||||
def SendMessage(self, hWnd, Msg, wParam, lParam):
|
||||
"""Wraps the windows API call of SendMessage"""
|
||||
return self.__SendMessage(hWnd, Msg, wParam, lParam)
|
||||
|
||||
def SendMessageTimeout(self, hWnd, Msg,
|
||||
wParam, lParam, fuFlags,
|
||||
uTimeout, lpdwResult = None):
|
||||
"""Wraps the windows API call of SendMessageTimeout"""
|
||||
idToRet = None
|
||||
try:
|
||||
idFromMsg = array.array('I', [0])
|
||||
result = idFromMsg.buffer_info()[0]
|
||||
response = self.__SendMessageTimeout(hWnd, Msg, wParam,
|
||||
lParam, fuFlags,
|
||||
uTimeout, result)
|
||||
if response == 0:
|
||||
raise Win32FuncException, "SendMessageTimeout TimedOut"
|
||||
|
||||
idToRet = idFromMsg[0]
|
||||
except TypeError:
|
||||
idToRet = self.__SendMessageTimeout(hWnd, Msg, wParam,
|
||||
lParam, fuFlags,
|
||||
uTimeout)
|
||||
|
||||
if lpdwResult is not None and lpdwResult.typecode == 'I':
|
||||
lpdwResult[0] = idToRet
|
||||
|
||||
return idToRet
|
||||
|
||||
def IsWindow(self, hWnd):
|
||||
"""Wraps the windows API call of IsWindow"""
|
||||
return self.__IsWindow(hWnd)
|
||||
|
||||
def RegisterWindowMessage(self, lpString):
|
||||
"""Wraps the windows API call of RegisterWindowMessage"""
|
||||
return self.__RegisterWindowMessage(self.__strType(lpString))
|
||||
|
||||
def GetWindowText(self, hWnd, lpString = None, nMaxCount = None):
|
||||
"""Wraps the windows API call of SendMessageTimeout"""
|
||||
text = ''
|
||||
if hWnd == 0:
|
||||
return text
|
||||
|
||||
if nMaxCount is None:
|
||||
nMaxCount = 1025
|
||||
|
||||
try:
|
||||
arrayType = 'c'
|
||||
if self.__strType == unicode:
|
||||
arrayType = 'u'
|
||||
path_string = array.array(arrayType, self.__strType('\x00') * nMaxCount)
|
||||
path_buffer = path_string.buffer_info()[0]
|
||||
result = self.__GetWindowText(hWnd,
|
||||
path_buffer,
|
||||
nMaxCount)
|
||||
if result > 0:
|
||||
if self.__strType == unicode:
|
||||
text = path_string[0:result].tounicode()
|
||||
else:
|
||||
text = path_string[0:result].tostring()
|
||||
except TypeError:
|
||||
text = self.__GetWindowText(hWnd)
|
||||
|
||||
if lpString is not None and lpString.typecode == 'c':
|
||||
lpdwResult[0:len(text)] = array.array('c', str(text));
|
||||
|
||||
if lpString is not None and lpString.typecode == 'u':
|
||||
lpdwResult[0:len(text)] = array.array('u', unicode(text));
|
||||
|
||||
return text
|
||||
|
||||
def __init__(self):
|
||||
"""Load up my needed functions"""
|
||||
# First see if they already have win32gui imported. If so use it.
|
||||
# This has to be checked first since the auto check looks for ctypes
|
||||
# first.
|
||||
try:
|
||||
self.__FindWindow = win32gui.FindWindow
|
||||
self.__FindWindowEx = win32gui.FindWindowEx
|
||||
self.__GetWindowText = win32gui.GetWindowText
|
||||
self.__IsWindow = win32gui.IsWindow
|
||||
self.__SendMessage = win32gui.SendMessage
|
||||
self.__SendMessageTimeout = win32gui.SendMessageTimeout
|
||||
self.__RegisterWindowMessage = win32gui.RegisterWindowMessage
|
||||
self.__strType = unicode
|
||||
|
||||
#Something threw a NameError, most likely the win32gui lines
|
||||
#so do auto check
|
||||
except NameError:
|
||||
try:
|
||||
from ctypes import windll
|
||||
self.__FindWindow = windll.user32.FindWindowW
|
||||
self.__FindWindowEx = windll.user32.FindWindowExW
|
||||
self.__GetWindowText = windll.user32.GetWindowTextW
|
||||
self.__IsWindow = windll.user32.IsWindow
|
||||
self.__SendMessage = windll.user32.SendMessageW
|
||||
self.__SendMessageTimeout = windll.user32.SendMessageTimeoutW
|
||||
self.__RegisterWindowMessage = windll.user32.RegisterWindowMessageW
|
||||
self.__strType = unicode
|
||||
|
||||
#FindWindowW wasn't found, look for FindWindowA
|
||||
except AttributeError:
|
||||
try:
|
||||
self.__FindWindow = windll.user32.FindWindowA
|
||||
self.__FindWindowEx = windll.user32.FindWindowExA
|
||||
self.__GetWindowText = windll.user32.GetWindowTextA
|
||||
self.__IsWindow = windll.user32.IsWindow
|
||||
self.__SendMessage = windll.user32.SendMessageA
|
||||
self.__SendMessageTimeout = windll.user32.SendMessageTimeoutA
|
||||
self.__RegisterWindowMessage = windll.user32.RegisterWindowMessageA
|
||||
# Couldn't find either so Die and tell user why.
|
||||
except AttributeError:
|
||||
import sys
|
||||
sys.stderr.write("Your Windows TM setup seems to be corrupt."+
|
||||
" No FindWindow found in user32.\n")
|
||||
sys.stderr.flush()
|
||||
sys.exit(3)
|
||||
|
||||
except ImportError:
|
||||
try:
|
||||
import win32gui
|
||||
self.__FindWindow = win32gui.FindWindow
|
||||
self.__FindWindowEx = win32gui.FindWindowEx
|
||||
self.__GetWindowText = win32gui.GetWindowText
|
||||
self.__IsWindow = win32gui.IsWindow
|
||||
self.__SendMessage = win32gui.SendMessage
|
||||
self.__SendMessageTimeout = win32gui.SendMessageTimeout
|
||||
self.__RegisterWindowMessage = win32gui.RegisterWindowMessage
|
||||
self.__strType = unicode
|
||||
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("You need to have either"+
|
||||
" ctypes or pywin32 installed.\n")
|
||||
sys.stderr.flush()
|
||||
#sys.exit(2)
|
||||
|
||||
|
||||
myWin32Funcs = Win32Funcs()
|
||||
|
||||
|
||||
SHOW = 1
|
||||
HIDE = 2
|
||||
UPDATE = 3
|
||||
IS_VISIBLE = 4
|
||||
GET_VERSION = 5
|
||||
REGISTER_CONFIG_WINDOW = 6
|
||||
REVOKE_CONFIG_WINDOW = 7
|
||||
REGISTER_ALERT = 8
|
||||
REVOKE_ALERT = 9
|
||||
REGISTER_CONFIG_WINDOW_2 = 10
|
||||
GET_VERSION_EX = 11
|
||||
SET_TIMEOUT = 12
|
||||
|
||||
EX_SHOW = 32
|
||||
|
||||
GLOBAL_MESSAGE = "SnarlGlobalMessage"
|
||||
GLOBAL_MSG = "SnarlGlobalEvent"
|
||||
|
||||
#Messages That may be received from Snarl
|
||||
SNARL_LAUNCHED = 1
|
||||
SNARL_QUIT = 2
|
||||
SNARL_ASK_APPLET_VER = 3
|
||||
SNARL_SHOW_APP_UI = 4
|
||||
|
||||
SNARL_NOTIFICATION_CLICKED = 32 #notification was right-clicked by user
|
||||
SNARL_NOTIFICATION_CANCELLED = SNARL_NOTIFICATION_CLICKED #Name clarified
|
||||
SNARL_NOTIFICATION_TIMED_OUT = 33
|
||||
SNARL_NOTIFICATION_ACK = 34 #notification was left-clicked by user
|
||||
|
||||
#Snarl Test Message
|
||||
WM_SNARLTEST = myWin32Funcs.WM_USER + 237
|
||||
|
||||
M_ABORTED = 0x80000007L
|
||||
M_ACCESS_DENIED = 0x80000009L
|
||||
M_ALREADY_EXISTS = 0x8000000CL
|
||||
M_BAD_HANDLE = 0x80000006L
|
||||
M_BAD_POINTER = 0x80000005L
|
||||
M_FAILED = 0x80000008L
|
||||
M_INVALID_ARGS = 0x80000003L
|
||||
M_NO_INTERFACE = 0x80000004L
|
||||
M_NOT_FOUND = 0x8000000BL
|
||||
M_NOT_IMPLEMENTED = 0x80000001L
|
||||
M_OK = 0x00000000L
|
||||
M_OUT_OF_MEMORY = 0x80000002L
|
||||
M_TIMED_OUT = 0x8000000AL
|
||||
|
||||
ErrorCodeRev = {
|
||||
0x80000007L : "M_ABORTED",
|
||||
0x80000009L : "M_ACCESS_DENIED",
|
||||
0x8000000CL : "M_ALREADY_EXISTS",
|
||||
0x80000006L : "M_BAD_HANDLE",
|
||||
0x80000005L : "M_BAD_POINTER",
|
||||
0x80000008L : "M_FAILED",
|
||||
0x80000003L : "M_INVALID_ARGS",
|
||||
0x80000004L : "M_NO_INTERFACE",
|
||||
0x8000000BL : "M_NOT_FOUND",
|
||||
0x80000001L : "M_NOT_IMPLEMENTED",
|
||||
0x00000000L : "M_OK",
|
||||
0x80000002L : "M_OUT_OF_MEMORY",
|
||||
0x8000000AL : "M_TIMED_OUT"
|
||||
}
|
||||
|
||||
class SnarlMessage(object):
|
||||
"""The main Snarl interface object.
|
||||
|
||||
ID = Snarl Message ID for most operations. See SDK for more info
|
||||
as to other values to put here.
|
||||
type = Snarl Message Type. Valid values are : SHOW, HIDE, UPDATE,
|
||||
IS_VISIBLE, GET_VERSION, REGISTER_CONFIG_WINDOW, REVOKE_CONFIG_WINDOW
|
||||
all which are constants in the PySnarl module.
|
||||
timeout = Timeout in seconds for the Snarl Message
|
||||
data = Snarl Message data. This is dependant upon message type. See SDK
|
||||
title = Snarl Message title.
|
||||
text = Snarl Message text.
|
||||
icon = Path to the icon to display in the Snarl Message.
|
||||
"""
|
||||
__msgType = 0
|
||||
__msgID = 0
|
||||
__msgTimeout = 0
|
||||
__msgData = 0
|
||||
__msgTitle = ""
|
||||
__msgText = ""
|
||||
__msgIcon = ""
|
||||
__msgClass = ""
|
||||
__msgExtra = ""
|
||||
__msgExtra2 = ""
|
||||
__msgRsvd1 = 0
|
||||
__msgRsvd2 = 0
|
||||
__msgHWnd = 0
|
||||
|
||||
lastKnownHWnd = 0
|
||||
|
||||
def getType(self):
|
||||
"""Type Attribute getter."""
|
||||
return self.__msgType
|
||||
def setType(self, value):
|
||||
"""Type Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgType = value
|
||||
type = property(getType, setType, doc="The Snarl Message Type")
|
||||
|
||||
def getID(self):
|
||||
"""ID Attribute getter."""
|
||||
return self.__msgID
|
||||
def setID(self, value):
|
||||
"""ID Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgID = value
|
||||
ID = property(getID, setID, doc="The Snarl Message ID")
|
||||
|
||||
def getTimeout(self):
|
||||
"""Timeout Attribute getter."""
|
||||
return self.__msgTimeout
|
||||
def updateTimeout(self, value):
|
||||
"""Timeout Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgTimeout = value
|
||||
timeout = property(getTimeout, updateTimeout,
|
||||
doc="The Snarl Message Timeout")
|
||||
|
||||
def getData(self):
|
||||
"""Data Attribute getter."""
|
||||
return self.__msgData
|
||||
def setData(self, value):
|
||||
"""Data Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgData = value
|
||||
data = property(getData, setData, doc="The Snarl Message Data")
|
||||
|
||||
def getTitle(self):
|
||||
"""Title Attribute getter."""
|
||||
return self.__msgTitle
|
||||
def setTitle(self, value):
|
||||
"""Title Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgTitle = value
|
||||
title = property(getTitle, setTitle, doc="The Snarl Message Title")
|
||||
|
||||
def getText(self):
|
||||
"""Text Attribute getter."""
|
||||
return self.__msgText
|
||||
def setText(self, value):
|
||||
"""Text Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgText = value
|
||||
text = property(getText, setText, doc="The Snarl Message Text")
|
||||
|
||||
def getIcon(self):
|
||||
"""Icon Attribute getter."""
|
||||
return self.__msgIcon
|
||||
def setIcon(self, value):
|
||||
"""Icon Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgIcon = value
|
||||
icon = property(getIcon, setIcon, doc="The Snarl Message Icon")
|
||||
|
||||
def getClass(self):
|
||||
"""Class Attribute getter."""
|
||||
return self.__msgClass
|
||||
def setClass(self, value):
|
||||
"""Class Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgClass = value
|
||||
msgclass = property(getClass, setClass, doc="The Snarl Message Class")
|
||||
|
||||
def getExtra(self):
|
||||
"""Extra Attribute getter."""
|
||||
return self.__msgExtra
|
||||
def setExtra(self, value):
|
||||
"""Extra Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgExtra = value
|
||||
extra = property(getExtra, setExtra, doc="Extra Info for the Snarl Message")
|
||||
|
||||
def getExtra2(self):
|
||||
"""Extra2 Attribute getter."""
|
||||
return self.__msgExtra2
|
||||
def setExtra2(self, value):
|
||||
"""Extra2 Attribute setter."""
|
||||
if( isinstance(value, basestring) ):
|
||||
self.__msgExtra2 = value
|
||||
extra2 = property(getExtra2, setExtra2,
|
||||
doc="More Extra Info for the Snarl Message")
|
||||
|
||||
def getRsvd1(self):
|
||||
"""Rsvd1 Attribute getter."""
|
||||
return self.__msgRsvd1
|
||||
def setRsvd1(self, value):
|
||||
"""Rsvd1 Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgRsvd1 = value
|
||||
rsvd1 = property(getRsvd1, setRsvd1, doc="The Snarl Message Field Rsvd1")
|
||||
|
||||
def getRsvd2(self):
|
||||
"""Rsvd2 Attribute getter."""
|
||||
return self.__msgRsvd2
|
||||
def setRsvd2(self, value):
|
||||
"""Rsvd2 Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgRsvd2 = value
|
||||
rsvd2 = property(getRsvd2, setRsvd2, doc="The Snarl Message Field Rsvd2")
|
||||
|
||||
def getHwnd(self):
|
||||
"""hWnd Attribute getter."""
|
||||
return self.__msgHWnd
|
||||
def setHwnd(self, value):
|
||||
"""hWnd Attribute setter."""
|
||||
if( isinstance(value, (int, long)) ):
|
||||
self.__msgHWnd = value
|
||||
|
||||
hWnd = property(getHwnd, setHwnd, doc="The hWnd of the window this message is being sent from")
|
||||
|
||||
|
||||
def __init__(self, title="", text="", icon="", msg_type=1, msg_id=0):
|
||||
self.__msgTimeout = 0
|
||||
self.__msgData = 0
|
||||
self.__msgClass = ""
|
||||
self.__msgExtra = ""
|
||||
self.__msgExtra2 = ""
|
||||
self.__msgRsvd1 = 0
|
||||
self.__msgRsvd2 = 0
|
||||
self.__msgType = msg_type
|
||||
self.__msgText = text
|
||||
self.__msgTitle = title
|
||||
self.__msgIcon = icon
|
||||
self.__msgID = msg_id
|
||||
|
||||
def createCopyStruct(self):
|
||||
"""Creates the struct to send as the copyData in the message."""
|
||||
return struct.pack("ILLL1024s1024s1024s1024s1024s1024sLL",
|
||||
self.__msgType,
|
||||
self.__msgID,
|
||||
self.__msgTimeout,
|
||||
self.__msgData,
|
||||
self.__msgTitle.encode('utf-8'),
|
||||
self.__msgText.encode('utf-8'),
|
||||
self.__msgIcon.encode('utf-8'),
|
||||
self.__msgClass.encode('utf-8'),
|
||||
self.__msgExtra.encode('utf-8'),
|
||||
self.__msgExtra2.encode('utf-8'),
|
||||
self.__msgRsvd1,
|
||||
self.__msgRsvd2
|
||||
)
|
||||
__lpData = None
|
||||
__cds = None
|
||||
|
||||
def packData(self, dwData):
|
||||
"""This packs the data in the necessary format for a
|
||||
WM_COPYDATA message."""
|
||||
self.__lpData = None
|
||||
self.__cds = None
|
||||
item = self.createCopyStruct()
|
||||
self.__lpData = array.array('c', item)
|
||||
lpData_ad = self.__lpData.buffer_info()[0]
|
||||
cbData = self.__lpData.buffer_info()[1]
|
||||
self.__cds = array.array('c',
|
||||
struct.pack("IIP",
|
||||
dwData,
|
||||
cbData,
|
||||
lpData_ad)
|
||||
)
|
||||
cds_ad = self.__cds.buffer_info()[0]
|
||||
return cds_ad
|
||||
|
||||
def reset(self):
|
||||
"""Reset this SnarlMessage to the default state."""
|
||||
self.__msgType = 0
|
||||
self.__msgID = 0
|
||||
self.__msgTimeout = 0
|
||||
self.__msgData = 0
|
||||
self.__msgTitle = ""
|
||||
self.__msgText = ""
|
||||
self.__msgIcon = ""
|
||||
self.__msgClass = ""
|
||||
self.__msgExtra = ""
|
||||
self.__msgExtra2 = ""
|
||||
self.__msgRsvd1 = 0
|
||||
self.__msgRsvd2 = 0
|
||||
|
||||
|
||||
def send(self, setid=True):
|
||||
"""Send this SnarlMessage to the Snarl window.
|
||||
Args:
|
||||
setid - Boolean defining whether or not to set the ID
|
||||
of this SnarlMessage to the return value of
|
||||
the SendMessage call. Default is True to
|
||||
make simple case of SHOW easy.
|
||||
"""
|
||||
hwnd = myWin32Funcs.FindWindow(None, "Snarl")
|
||||
if myWin32Funcs.IsWindow(hwnd):
|
||||
if self.type == REGISTER_CONFIG_WINDOW or self.type == REGISTER_CONFIG_WINDOW_2:
|
||||
self.hWnd = self.data
|
||||
try:
|
||||
response = myWin32Funcs.SendMessageTimeout(hwnd,
|
||||
myWin32Funcs.WM_COPYDATA,
|
||||
self.hWnd, self.packData(2),
|
||||
2, 500)
|
||||
except Win32FuncException:
|
||||
return False
|
||||
|
||||
idFromMsg = response
|
||||
if setid:
|
||||
self.ID = idFromMsg
|
||||
return True
|
||||
else:
|
||||
return idFromMsg
|
||||
print "No snarl window found"
|
||||
return False
|
||||
|
||||
def hide(self):
|
||||
"""Hide this message. Type will revert to type before calling hide
|
||||
to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = HIDE
|
||||
retVal = bool(self.send(False))
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def isVisible(self):
|
||||
"""Is this message visible. Type will revert to type before calling
|
||||
hide to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = IS_VISIBLE
|
||||
retVal = bool(self.send(False))
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def update(self, title=None, text=None, icon=None):
|
||||
"""Update this message with given title and text. Type will revert
|
||||
to type before calling hide to allow for better reuse of object."""
|
||||
oldType = self.__msgType
|
||||
self.__msgType = UPDATE
|
||||
if text:
|
||||
self.__msgText = text
|
||||
if title:
|
||||
self.__msgTitle = title
|
||||
if icon:
|
||||
self.__msgIcon = icon
|
||||
retVal = self.send(False)
|
||||
self.__msgType = oldType
|
||||
return retVal
|
||||
|
||||
def setTimeout(self, timeout):
|
||||
"""Set the timeout in seconds of the message"""
|
||||
oldType = self.__msgType
|
||||
oldData = self.__msgData
|
||||
self.__msgType = SET_TIMEOUT
|
||||
#self.timeout = timeout
|
||||
#self.__msgData = self.__msgTimeout
|
||||
self.__msgData = timeout
|
||||
retVal = self.send(False)
|
||||
self.__msgType = oldType
|
||||
self.__msgData = oldData
|
||||
return retVal
|
||||
|
||||
def show(self, timeout=None, title=None,
|
||||
text=None, icon=None,
|
||||
replyWindow=None, replyMsg=None, msgclass=None, soundPath=None):
|
||||
"""Show a message"""
|
||||
oldType = self.__msgType
|
||||
oldTimeout = self.__msgTimeout
|
||||
self.__msgType = SHOW
|
||||
if text:
|
||||
self.__msgText = text
|
||||
if title:
|
||||
self.__msgTitle = title
|
||||
if timeout:
|
||||
self.__msgTimeout = timeout
|
||||
if icon:
|
||||
self.__msgIcon = icon
|
||||
if replyWindow:
|
||||
self.__msgID = replyMsg
|
||||
if replyMsg:
|
||||
self.__msgData = replyWindow
|
||||
if soundPath:
|
||||
self.__msgExtra = soundPath
|
||||
if msgclass:
|
||||
self.__msgClass = msgclass
|
||||
|
||||
if ((self.__msgClass and self.__msgClass != "") or
|
||||
(self.__msgExtra and self.__msgExtra != "")):
|
||||
self.__msgType = EX_SHOW
|
||||
|
||||
|
||||
retVal = bool(self.send())
|
||||
self.__msgType = oldType
|
||||
self.__msgTimeout = oldTimeout
|
||||
return retVal
|
||||
|
||||
|
||||
def snGetVersion():
|
||||
""" Get the version of Snarl that is running as a tuple. (Major, Minor)
|
||||
|
||||
If Snarl is not running or there was an error it will
|
||||
return False."""
|
||||
msg = SnarlMessage(msg_type=GET_VERSION)
|
||||
version = msg.send(False)
|
||||
if not version:
|
||||
return False
|
||||
return (HIWORD(version), LOWORD(version))
|
||||
|
||||
def snGetVersionEx():
|
||||
""" Get the internal version of Snarl that is running.
|
||||
|
||||
If Snarl is not running or there was an error it will
|
||||
return False."""
|
||||
sm = SnarlMessage(msg_type=GET_VERSION_EX)
|
||||
verNum = sm.send(False)
|
||||
if not verNum:
|
||||
return False
|
||||
return verNum
|
||||
|
||||
def snGetGlobalMessage():
|
||||
"""Get the Snarl global message id from windows."""
|
||||
return myWin32Funcs.RegisterWindowMessage(GLOBAL_MSG)
|
||||
|
||||
def snShowMessage(title, text, timeout=0, iconPath="",
|
||||
replyWindow=0, replyMsg=0):
|
||||
"""Show a message using Snarl and return its ID. See SDK for arguments."""
|
||||
sm = SnarlMessage( title, text, iconPath, msg_id=replyMsg)
|
||||
sm.data = replyWindow
|
||||
if sm.show(timeout):
|
||||
return sm.ID
|
||||
else:
|
||||
return False
|
||||
|
||||
def snShowMessageEx(msgClass, title, text, timeout=0, iconPath="",
|
||||
replyWindow=0, replyMsg=0, soundFile=None, hWndFrom=None):
|
||||
"""Show a message using Snarl and return its ID. See SDK for arguments.
|
||||
One added argument is hWndFrom that allows one to make the messages appear
|
||||
to come from a specific window. This window should be the one you registered
|
||||
earlier with RegisterConfig"""
|
||||
sm = SnarlMessage( title, text, iconPath, msg_id=replyMsg)
|
||||
sm.data = replyWindow
|
||||
if hWndFrom is not None:
|
||||
sm.hWnd = hWndFrom
|
||||
else:
|
||||
sm.hWnd = SnarlMessage.lastKnownHWnd
|
||||
if sm.show(timeout, msgclass=msgClass, soundPath=soundFile):
|
||||
return sm.ID
|
||||
else:
|
||||
return False
|
||||
|
||||
def snUpdateMessage(msgId, msgTitle, msgText, icon=None):
|
||||
"""Update a message"""
|
||||
sm = SnarlMessage(msg_id=msgId)
|
||||
if icon:
|
||||
sm.icon = icon
|
||||
return sm.update(msgTitle, msgText)
|
||||
|
||||
def snHideMessage(msgId):
|
||||
"""Hide a message"""
|
||||
return SnarlMessage(msg_id=msgId).hide()
|
||||
|
||||
def snSetTimeout(msgId, timeout):
|
||||
"""Update the timeout of a message already shown."""
|
||||
sm = SnarlMessage(msg_id=msgId)
|
||||
return sm.setTimeout(timeout)
|
||||
|
||||
def snIsMessageVisible(msgId):
|
||||
"""Returns True if the message is visible False otherwise."""
|
||||
return SnarlMessage(msg_id=msgId).isVisible()
|
||||
|
||||
def snRegisterConfig(replyWnd, appName, replyMsg):
|
||||
"""Register a config window. See SDK for more info."""
|
||||
global lastRegisteredSnarlMsg
|
||||
sm = SnarlMessage(msg_type=REGISTER_CONFIG_WINDOW,
|
||||
title=appName,
|
||||
msg_id=replyMsg)
|
||||
sm.data = replyWnd
|
||||
SnarlMessage.lastKnownHWnd = replyWnd
|
||||
|
||||
return sm.send(False)
|
||||
|
||||
def snRegisterConfig2(replyWnd, appName, replyMsg, icon):
|
||||
"""Register a config window. See SDK for more info."""
|
||||
global lastRegisteredSnarlMsg
|
||||
sm = SnarlMessage(msg_type=REGISTER_CONFIG_WINDOW_2,
|
||||
title=appName,
|
||||
msg_id=replyMsg,
|
||||
icon=icon)
|
||||
sm.data = replyWnd
|
||||
SnarlMessage.lastKnownHWnd = replyWnd
|
||||
return sm.send(False)
|
||||
|
||||
def snRegisterAlert(appName, classStr) :
|
||||
"""Register an alert for an already registered config. See SDK for more info."""
|
||||
sm = SnarlMessage(msg_type=REGISTER_ALERT,
|
||||
title=appName,
|
||||
text=classStr)
|
||||
return sm.send(False)
|
||||
|
||||
def snRevokeConfig(replyWnd):
|
||||
"""Revoke a config window"""
|
||||
sm = SnarlMessage(msg_type=REVOKE_CONFIG_WINDOW)
|
||||
sm.data = replyWnd
|
||||
if replyWnd == SnarlMessage.lastKnownHWnd:
|
||||
SnarlMessage.lastKnownHWnd = 0
|
||||
return sm.send(False)
|
||||
|
||||
def snGetSnarlWindow():
|
||||
"""Returns the hWnd of the snarl window"""
|
||||
return myWin32Funcs.FindWindow(None, "Snarl")
|
||||
|
||||
def snGetAppPath():
|
||||
"""Returns the application path of the currently running snarl window"""
|
||||
app_path = None
|
||||
snarl_handle = snGetSnarlWindow()
|
||||
if snarl_handle != 0:
|
||||
pathwin_handle = myWin32Funcs.FindWindowEx(snarl_handle,
|
||||
0,
|
||||
"static",
|
||||
None)
|
||||
if pathwin_handle != 0:
|
||||
try:
|
||||
result = myWin32Funcs.GetWindowText(pathwin_handle)
|
||||
app_path = result
|
||||
except Win32FuncException:
|
||||
pass
|
||||
|
||||
|
||||
return app_path
|
||||
|
||||
def snGetIconsPath():
|
||||
"""Returns the path to the icons of the program"""
|
||||
s = snGetAppPath()
|
||||
if s is None:
|
||||
return ""
|
||||
else:
|
||||
return s + "etc\\icons\\"
|
||||
|
||||
def snSendTestMessage(data=None):
|
||||
"""Sends a test message to Snarl. Used to make sure the
|
||||
api is connecting"""
|
||||
param = 0
|
||||
command = 0
|
||||
if data:
|
||||
param = struct.pack("I", data)
|
||||
command = 1
|
||||
myWin32Funcs.SendMessage(snGetSnarlWindow(), WM_SNARLTEST, command, param)
|
|
@ -1 +0,0 @@
|
|||
from plugin import SnarlNotificationsPlugin
|
|
@ -1,11 +0,0 @@
|
|||
[info]
|
||||
name: Snarl Notifications
|
||||
short_name: snarl_notifications
|
||||
version: 0.1
|
||||
description: Shows events notification using Snarl (http://www.fullphat.net/) under Windows. Snarl needs to be installed in system.
|
||||
PySnarl bindings are used (http://code.google.com/p/pysnarl/).
|
||||
authors = Mateusz Biliński <mateusz@bilinski.it>
|
||||
homepage = http://blog.bilinski.it
|
||||
|
||||
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##
|
||||
## 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/>.
|
||||
##
|
||||
'''
|
||||
Events notifications using Snarl
|
||||
|
||||
Fancy events notifications under Windows using Snarl infrastructure.
|
||||
|
||||
:note: plugin is at proof-of-concept state.
|
||||
|
||||
:author: Mateusz Biliński <mateusz@bilinski.it>
|
||||
:since: 15th August 2008
|
||||
:copyright: Copyright (2008) Mateusz Biliński <mateusz@bilinski.it>
|
||||
:license: GPL
|
||||
'''
|
||||
|
||||
import new
|
||||
from pprint import pformat
|
||||
|
||||
#import PySnarl
|
||||
|
||||
from common import gajim
|
||||
from plugins import GajimPlugin
|
||||
from plugins.helpers import log_calls, log
|
||||
from common import ged
|
||||
|
||||
class SnarlNotificationsPlugin(GajimPlugin):
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def init(self):
|
||||
self.description = _('Shows events notification using Snarl '
|
||||
'(http://www.fullphat.net/) under Windows. '
|
||||
'Snarl needs to be installed in system.\n'
|
||||
'PySnarl bindings are used (http://code.google.com/p/pysnarl/).')
|
||||
self.config_dialog = None
|
||||
#self.gui_extension_points = {}
|
||||
#self.config_default_values = {}
|
||||
|
||||
self.events_handlers = {'notification' : (ged.POSTCORE, self.notif)}
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def activate(self):
|
||||
pass
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def deactivate(self):
|
||||
pass
|
||||
|
||||
@log_calls('SnarlNotificationsPlugin')
|
||||
def notif(self, obj):
|
||||
print "Event '%s' occured.\n\n===\n" % obj.popup_event_type
|
||||
|
||||
#if PySnarl.snGetVersion() != False:
|
||||
#(major, minor) = PySnarl.snGetVersion()
|
||||
#print "Found Snarl version",str(major)+"."+str(minor),"running."
|
||||
#PySnarl.snShowMessage(obj.popup_title, obj.popup_text)
|
||||
#else:
|
||||
#print "Sorry Snarl does not appear to be running"
|
|
@ -1,3 +0,0 @@
|
|||
data/gajim.desktop.in
|
||||
src/eggtrayicon.c
|
||||
src/ipython_view.py
|
|
@ -1,6 +1,6 @@
|
|||
## setup_win32.py (run me as python setup_win32.py py2exe -O2)
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
## Stephan Erb <steve-e AT h3c.de>
|
||||
##
|
||||
|
@ -99,7 +99,7 @@ class CommandWindow:
|
|||
self.stage1()
|
||||
|
||||
# displaying the window
|
||||
self.window.set_title('Ad-hoc Commands - Gajim')
|
||||
self.window.set_title(_('Ad-hoc Commands - Gajim'))
|
||||
self.xml.connect_signals(self)
|
||||
self.window.show_all()
|
||||
|
||||
|
@ -415,7 +415,7 @@ class CommandWindow:
|
|||
return
|
||||
self.data_form_widget.show()
|
||||
if self.data_form_widget.title:
|
||||
self.window.set_title('%s - Ad-hoc Commands - Gajim' % \
|
||||
self.window.set_title(_('%s - Ad-hoc Commands - Gajim') % \
|
||||
self.data_form_widget.title)
|
||||
else:
|
||||
self.data_form_widget.hide()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2005 Travis Shirk <travis AT pobox.com>
|
||||
## Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
@ -81,6 +81,7 @@ class AdvancedConfigurationWindow(object):
|
|||
self.entry = self.xml.get_object('advanced_entry')
|
||||
self.desc_label = self.xml.get_object('advanced_desc_label')
|
||||
self.restart_label = self.xml.get_object('restart_label')
|
||||
self.reset_button = self.xml.get_object('reset_button')
|
||||
|
||||
# Format:
|
||||
# key = option name (root/subopt/opt separated by \n then)
|
||||
|
@ -174,6 +175,13 @@ class AdvancedConfigurationWindow(object):
|
|||
else:
|
||||
#we talk about option description in advanced configuration editor
|
||||
self.desc_label.set_text(_('(None)'))
|
||||
if len(opt_path) == 3 or (len(opt_path) == 1 and not \
|
||||
model.iter_has_child(iter_)):
|
||||
self.reset_button.set_sensitive(True)
|
||||
else:
|
||||
self.reset_button.set_sensitive(False)
|
||||
else:
|
||||
self.reset_button.set_sensitive(False)
|
||||
|
||||
def remember_option(self, option, oldval, newval):
|
||||
if option in self.changed_opts:
|
||||
|
@ -195,7 +203,6 @@ class AdvancedConfigurationWindow(object):
|
|||
optname = optnamerow[0].decode('utf-8')
|
||||
keyrow = self.model[modelpath[:2]]
|
||||
key = keyrow[0].decode('utf-8')
|
||||
gajim.config.get_desc_per(optname, key, option)
|
||||
self.remember_option(option + '\n' + key + '\n' + optname,
|
||||
modelrow[1], newval)
|
||||
gajim.config.set_per(optname, key, option, newval)
|
||||
|
@ -244,6 +251,42 @@ class AdvancedConfigurationWindow(object):
|
|||
def on_advanced_configuration_window_destroy(self, widget):
|
||||
del gajim.interface.instances['advanced_config']
|
||||
|
||||
def on_reset_button_clicked(self, widget):
|
||||
model, iter_ = self.treeview.get_selection().get_selected()
|
||||
# Check for GtkTreeIter
|
||||
if iter_:
|
||||
path = model.get_path(iter_)
|
||||
opt_path = self.get_option_path(model, iter_)
|
||||
if len(opt_path) == 1:
|
||||
default = gajim.config.get_default(opt_path[0])
|
||||
elif len(opt_path) == 3:
|
||||
default = gajim.config.get_default_per(opt_path[2], opt_path[0])
|
||||
|
||||
if model[iter_][C_TYPE] == self.types['boolean']:
|
||||
if self.right_true_dict[default] == model[iter_][C_VALUE]:
|
||||
return
|
||||
modelpath = self.modelfilter.convert_path_to_child_path(path)
|
||||
modelrow = self.model[modelpath]
|
||||
option = modelrow[0].decode('utf-8')
|
||||
if len(modelpath) > 1:
|
||||
optnamerow = self.model[modelpath[0]]
|
||||
optname = optnamerow[0].decode('utf-8')
|
||||
keyrow = self.model[modelpath[:2]]
|
||||
key = keyrow[0].decode('utf-8')
|
||||
self.remember_option(option + '\n' + key + '\n' + optname,
|
||||
modelrow[C_VALUE], default)
|
||||
gajim.config.set_per(optname, key, option, default)
|
||||
else:
|
||||
self.remember_option(option, modelrow[C_VALUE], default)
|
||||
gajim.config.set(option, default)
|
||||
gajim.interface.save_config()
|
||||
modelrow[C_VALUE] = self.right_true_dict[default]
|
||||
self.check_for_restart()
|
||||
else:
|
||||
if str(default) == model[iter_][C_VALUE]:
|
||||
return
|
||||
self.on_config_edited(None, path, str(default))
|
||||
|
||||
def on_advanced_close_button_clicked(self, widget):
|
||||
self.window.destroy()
|
||||
|
||||
|
@ -254,14 +297,18 @@ class AdvancedConfigurationWindow(object):
|
|||
newparent = self.model.append(parent, [name, '', ''])
|
||||
self.fill_model(item, newparent)
|
||||
else: # Leaf
|
||||
type_ = self.types[option[OPT_TYPE][0]]
|
||||
if len(item) == 1:
|
||||
type_ = self.types[gajim.config.get_type(name)]
|
||||
elif len(item) == 3:
|
||||
type_ = self.types[gajim.config.get_type_per(item[0],
|
||||
item[2])]
|
||||
if name == 'password':
|
||||
value = _('Hidden')
|
||||
else:
|
||||
if type_ == self.types['boolean']:
|
||||
value = self.right_true_dict[option[OPT_VAL]]
|
||||
value = self.right_true_dict[option]
|
||||
else:
|
||||
value = option[OPT_VAL]
|
||||
value = option
|
||||
self.model.append(parent, [name, value, type_])
|
||||
|
||||
def visible_func(self, model, treeiter):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/atom_window.py
|
||||
##
|
||||
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/cell_renderer_image.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/chat_control.py
|
||||
##
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
|
@ -56,6 +56,7 @@ from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
|
|||
from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_ICE_UDP, NS_JINGLE_FILE_TRANSFER
|
||||
from common.xmpp.protocol import NS_CHATSTATES
|
||||
from common.connection_handlers_events import MessageOutgoingEvent
|
||||
from common.exceptions import GajimGeneralException
|
||||
|
||||
from command_system.implementation.middleware import ChatCommandProcessor
|
||||
from command_system.implementation.middleware import CommandTools
|
||||
|
@ -469,7 +470,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.user_nick = None
|
||||
|
||||
self.smooth = True
|
||||
self.msg_textview.grab_focus()
|
||||
|
||||
self.command_hits = []
|
||||
self.last_key_tabs = False
|
||||
|
@ -864,7 +864,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
|
||||
def send_message(self, message, keyID='', type_='chat', chatstate=None,
|
||||
msg_id=None, resource=None, xhtml=None, callback=None, callback_args=[],
|
||||
process_commands=True):
|
||||
process_commands=True, attention=False):
|
||||
"""
|
||||
Send the given message to the active tab. Doesn't return None if error
|
||||
"""
|
||||
|
@ -881,7 +881,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
keyID=keyID, type_=type_, chatstate=chatstate, msg_id=msg_id,
|
||||
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
|
||||
label=label, callback=callback, callback_args=callback_args,
|
||||
control=self))
|
||||
control=self, attention=attention))
|
||||
|
||||
# Record the history of sent messages
|
||||
self.save_message(message, 'sent')
|
||||
|
@ -920,10 +920,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.received_history_pos = pos
|
||||
|
||||
def print_conversation_line(self, text, kind, name, tim,
|
||||
other_tags_for_name=[], other_tags_for_time=[],
|
||||
other_tags_for_text=[], count_as_new=True, subject=None,
|
||||
old_kind=None, xhtml=None, simple=False, xep0184_id=None,
|
||||
graphics=True, displaymarking=None):
|
||||
other_tags_for_name=[], other_tags_for_time=[], other_tags_for_text=[],
|
||||
count_as_new=True, subject=None, old_kind=None, xhtml=None, simple=False,
|
||||
xep0184_id=None, graphics=True, displaymarking=None, msg_id=None):
|
||||
"""
|
||||
Print 'chat' type messages
|
||||
"""
|
||||
|
@ -934,9 +933,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
if self.was_at_the_end or kind == 'outgoing':
|
||||
end = True
|
||||
textview.print_conversation_line(text, jid, kind, name, tim,
|
||||
other_tags_for_name, other_tags_for_time, other_tags_for_text,
|
||||
subject, old_kind, xhtml, simple=simple, graphics=graphics,
|
||||
displaymarking=displaymarking)
|
||||
other_tags_for_name, other_tags_for_time, other_tags_for_text,
|
||||
subject, old_kind, xhtml, simple=simple, graphics=graphics,
|
||||
displaymarking=displaymarking)
|
||||
|
||||
if xep0184_id is not None:
|
||||
textview.show_xep0184_warning(xep0184_id)
|
||||
|
@ -979,18 +978,18 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
type_ = 'printed_' + self.type_id
|
||||
event = 'message_received'
|
||||
show_in_roster = notify.get_show_in_roster(event,
|
||||
self.account, self.contact, self.session)
|
||||
self.account, self.contact, self.session)
|
||||
show_in_systray = notify.get_show_in_systray(event,
|
||||
self.account, self.contact, type_)
|
||||
self.account, self.contact, type_)
|
||||
|
||||
event = gajim.events.create_event(type_, (self,),
|
||||
show_in_roster=show_in_roster,
|
||||
event = gajim.events.create_event(type_, (text, subject, self,
|
||||
msg_id), show_in_roster=show_in_roster,
|
||||
show_in_systray=show_in_systray)
|
||||
gajim.events.add_event(self.account, full_jid, event)
|
||||
# We need to redraw contact if we show in roster
|
||||
if show_in_roster:
|
||||
gajim.interface.roster.draw_contact(self.contact.jid,
|
||||
self.account)
|
||||
self.account)
|
||||
|
||||
if not self.parent_win:
|
||||
return
|
||||
|
@ -1437,22 +1436,22 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
def __init__(self, parent_win, contact, acct, session, resource=None):
|
||||
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
|
||||
'chat_control', contact, acct, resource)
|
||||
'chat_control', contact, acct, resource)
|
||||
|
||||
self.gpg_is_active = False
|
||||
# for muc use:
|
||||
# widget = self.xml.get_object('muc_window_actions_button')
|
||||
self.actions_button = self.xml.get_object('message_window_actions_button')
|
||||
id_ = self.actions_button.connect('clicked',
|
||||
self.on_actions_button_clicked)
|
||||
self.on_actions_button_clicked)
|
||||
self.handlers[id_] = self.actions_button
|
||||
|
||||
self._formattings_button = self.xml.get_object('formattings_button')
|
||||
|
||||
self._add_to_roster_button = self.xml.get_object(
|
||||
'add_to_roster_button')
|
||||
'add_to_roster_button')
|
||||
id_ = self._add_to_roster_button.connect('clicked',
|
||||
self._on_add_to_roster_menuitem_activate)
|
||||
self._on_add_to_roster_menuitem_activate)
|
||||
self.handlers[id_] = self._add_to_roster_button
|
||||
|
||||
self._audio_button = self.xml.get_object('audio_togglebutton')
|
||||
|
@ -1460,14 +1459,14 @@ class ChatControl(ChatControlBase):
|
|||
self.handlers[id_] = self._audio_button
|
||||
# add a special img
|
||||
gtkgui_helpers.add_image_to_button(self._audio_button,
|
||||
'gajim-mic_inactive')
|
||||
'gajim-mic_inactive')
|
||||
|
||||
self._video_button = self.xml.get_object('video_togglebutton')
|
||||
id_ = self._video_button.connect('toggled', self.on_video_button_toggled)
|
||||
self.handlers[id_] = self._video_button
|
||||
# add a special img
|
||||
gtkgui_helpers.add_image_to_button(self._video_button,
|
||||
'gajim-cam_inactive')
|
||||
'gajim-cam_inactive')
|
||||
|
||||
self._send_file_button = self.xml.get_object('send_file_button')
|
||||
# add a special img for send file button
|
||||
|
@ -1476,30 +1475,30 @@ class ChatControl(ChatControlBase):
|
|||
img.set_from_file(path_to_upload_img)
|
||||
self._send_file_button.set_image(img)
|
||||
id_ = self._send_file_button.connect('clicked',
|
||||
self._on_send_file_menuitem_activate)
|
||||
self._on_send_file_menuitem_activate)
|
||||
self.handlers[id_] = self._send_file_button
|
||||
|
||||
self._convert_to_gc_button = self.xml.get_object(
|
||||
'convert_to_gc_button')
|
||||
'convert_to_gc_button')
|
||||
id_ = self._convert_to_gc_button.connect('clicked',
|
||||
self._on_convert_to_gc_menuitem_activate)
|
||||
self._on_convert_to_gc_menuitem_activate)
|
||||
self.handlers[id_] = self._convert_to_gc_button
|
||||
|
||||
contact_information_button = self.xml.get_object(
|
||||
'contact_information_button')
|
||||
id_ = contact_information_button.connect('clicked',
|
||||
self._on_contact_information_menuitem_activate)
|
||||
self.handlers[id_] = contact_information_button
|
||||
self._contact_information_button = self.xml.get_object(
|
||||
'contact_information_button')
|
||||
id_ = self._contact_information_button.connect('clicked',
|
||||
self._on_contact_information_menuitem_activate)
|
||||
self.handlers[id_] = self._contact_information_button
|
||||
|
||||
compact_view = gajim.config.get('compact_view')
|
||||
self.chat_buttons_set_visible(compact_view)
|
||||
self.widget_set_visible(self.xml.get_object('banner_eventbox'),
|
||||
gajim.config.get('hide_chat_banner'))
|
||||
gajim.config.get('hide_chat_banner'))
|
||||
|
||||
self.authentication_button = self.xml.get_object(
|
||||
'authentication_button')
|
||||
'authentication_button')
|
||||
id_ = self.authentication_button.connect('clicked',
|
||||
self._on_authentication_button_clicked)
|
||||
self._on_authentication_button_clicked)
|
||||
self.handlers[id_] = self.authentication_button
|
||||
|
||||
# Add lock image to show chat encryption
|
||||
|
@ -1541,31 +1540,31 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
# Hook up signals
|
||||
id_ = self.parent_win.window.connect('motion-notify-event',
|
||||
self._on_window_motion_notify)
|
||||
self._on_window_motion_notify)
|
||||
self.handlers[id_] = self.parent_win.window
|
||||
message_tv_buffer = self.msg_textview.get_buffer()
|
||||
id_ = message_tv_buffer.connect('changed',
|
||||
self._on_message_tv_buffer_changed)
|
||||
self._on_message_tv_buffer_changed)
|
||||
self.handlers[id_] = message_tv_buffer
|
||||
|
||||
widget = self.xml.get_object('avatar_eventbox')
|
||||
widget.set_property('height-request', gajim.config.get(
|
||||
'chat_avatar_height'))
|
||||
'chat_avatar_height'))
|
||||
id_ = widget.connect('enter-notify-event',
|
||||
self.on_avatar_eventbox_enter_notify_event)
|
||||
self.on_avatar_eventbox_enter_notify_event)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
id_ = widget.connect('leave-notify-event',
|
||||
self.on_avatar_eventbox_leave_notify_event)
|
||||
self.on_avatar_eventbox_leave_notify_event)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
id_ = widget.connect('button-press-event',
|
||||
self.on_avatar_eventbox_button_press_event)
|
||||
self.on_avatar_eventbox_button_press_event)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
widget = self.xml.get_object('location_eventbox')
|
||||
id_ = widget.connect('button-release-event',
|
||||
self.on_location_eventbox_button_release_event)
|
||||
self.on_location_eventbox_button_release_event)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
for key in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'):
|
||||
|
@ -1614,7 +1613,7 @@ class ChatControl(ChatControlBase):
|
|||
if not resource:
|
||||
resource = contact.resource
|
||||
session = gajim.connections[self.account].find_controlless_session(
|
||||
self.contact.jid, resource)
|
||||
self.contact.jid, resource)
|
||||
|
||||
self.setup_seclabel(self.xml.get_object('label_selector'))
|
||||
if session:
|
||||
|
@ -1627,8 +1626,7 @@ class ChatControl(ChatControlBase):
|
|||
# Enable encryption if needed
|
||||
self.no_autonegotiation = False
|
||||
e2e_is_active = self.session and self.session.enable_encryption
|
||||
gpg_pref = gajim.config.get_per('contacts', contact.jid,
|
||||
'gpg_enabled')
|
||||
gpg_pref = gajim.config.get_per('contacts', contact.jid, 'gpg_enabled')
|
||||
|
||||
# try GPG first
|
||||
if not e2e_is_active and gpg_pref and \
|
||||
|
@ -1637,30 +1635,30 @@ class ChatControl(ChatControlBase):
|
|||
self.gpg_is_active = True
|
||||
gajim.encrypted_chats[self.account].append(contact.jid)
|
||||
msg = _('GPG encryption enabled')
|
||||
ChatControlBase.print_conversation_line(self, msg,
|
||||
'status', '', None)
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '',
|
||||
None)
|
||||
|
||||
if self.session:
|
||||
self.session.loggable = gajim.config.get_per('accounts',
|
||||
self.account, 'log_encrypted_sessions')
|
||||
self.account, 'log_encrypted_sessions')
|
||||
# GPG is always authenticated as we use GPG's WoT
|
||||
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active,
|
||||
self.session and self.session.is_loggable(), True)
|
||||
self.session and self.session.is_loggable(), True)
|
||||
|
||||
self.update_ui()
|
||||
# restore previous conversation
|
||||
self.restore_conversation()
|
||||
self.msg_textview.grab_focus()
|
||||
|
||||
# change tooltip text for audio and video buttons if python-farsight is
|
||||
# change tooltip text for audio and video buttons if python-farstream is
|
||||
# not installed
|
||||
if not gajim.HAVE_FARSIGHT:
|
||||
if not gajim.HAVE_FARSTREAM:
|
||||
tooltip_text = self._audio_button.get_tooltip_text()
|
||||
self._audio_button.set_tooltip_text(
|
||||
'%s\n%s' % (tooltip_text, _('Requires python-farsight.')))
|
||||
'%s\n%s' % (tooltip_text, _('Requires python-farstream.')))
|
||||
tooltip_text = self._video_button.get_tooltip_text()
|
||||
self._video_button.set_tooltip_text(
|
||||
'%s\n%s' % (tooltip_text, _('Requires python-farsight.')))
|
||||
'%s\n%s' % (tooltip_text, _('Requires python-farstream.')))
|
||||
|
||||
gajim.ged.register_event_handler('pep-received', ged.GUI1,
|
||||
self._nec_pep_received)
|
||||
|
@ -1708,7 +1706,7 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
# Jingle detection
|
||||
if self.contact.supports(NS_JINGLE_ICE_UDP) and \
|
||||
gajim.HAVE_FARSIGHT and self.contact.resource:
|
||||
gajim.HAVE_FARSTREAM and self.contact.resource:
|
||||
self.audio_available = self.contact.supports(NS_JINGLE_RTP_AUDIO)
|
||||
self.video_available = self.contact.supports(NS_JINGLE_RTP_VIDEO)
|
||||
else:
|
||||
|
@ -1724,18 +1722,19 @@ class ChatControl(ChatControlBase):
|
|||
self._video_button.set_sensitive(self.video_available)
|
||||
|
||||
# Send file
|
||||
if (self.contact.supports(NS_FILE) or self.contact.supports(NS_JINGLE_FILE_TRANSFER)) and self.contact.resource:
|
||||
if (self.contact.supports(NS_FILE) or self.contact.supports(NS_JINGLE_FILE_TRANSFER)) and (self.type_id == 'chat' or \
|
||||
self.gc_contact.resource):
|
||||
self._send_file_button.set_sensitive(True)
|
||||
self._send_file_button.set_tooltip_text('')
|
||||
else:
|
||||
self._send_file_button.set_sensitive(False)
|
||||
if not (self.contact.supports(NS_FILE) or self.contact.supports(NS_JINGLE_FILE_TRANSFER)):
|
||||
self._send_file_button.set_tooltip_text(_(
|
||||
"This contact does not support file transfer."))
|
||||
"This contact does not support file transfer."))
|
||||
else:
|
||||
self._send_file_button.set_tooltip_text(
|
||||
_("You need to know the real JID of the contact to send him or "
|
||||
"her a file."))
|
||||
_("You need to know the real JID of the contact to send "
|
||||
"him or her a file."))
|
||||
|
||||
# Convert to GC
|
||||
if self.contact.supports(NS_MUC):
|
||||
|
@ -1743,6 +1742,13 @@ class ChatControl(ChatControlBase):
|
|||
else:
|
||||
self._convert_to_gc_button.set_sensitive(False)
|
||||
|
||||
# Information
|
||||
if gajim.account_is_disconnected(self.account):
|
||||
self._contact_information_button.set_sensitive(False)
|
||||
else:
|
||||
self._contact_information_button.set_sensitive(True)
|
||||
|
||||
|
||||
def update_all_pep_types(self):
|
||||
for pep_type in self._pep_images:
|
||||
self.update_pep(pep_type)
|
||||
|
@ -2230,7 +2236,7 @@ class ChatControl(ChatControlBase):
|
|||
dialogs.ESessionInfoWindow(self.session)
|
||||
|
||||
def send_message(self, message, keyID='', chatstate=None, xhtml=None,
|
||||
process_commands=True):
|
||||
process_commands=True, attention=False):
|
||||
"""
|
||||
Send a message to contact
|
||||
"""
|
||||
|
@ -2281,7 +2287,8 @@ class ChatControl(ChatControlBase):
|
|||
ChatControlBase.send_message(self, message, keyID, type_='chat',
|
||||
chatstate=chatstate_to_send, xhtml=xhtml, callback=_on_sent,
|
||||
callback_args=[contact, message, encrypted, xhtml,
|
||||
self.get_seclabel()], process_commands=process_commands)
|
||||
self.get_seclabel()], process_commands=process_commands,
|
||||
attention=attention)
|
||||
|
||||
def check_for_possible_paused_chatstate(self, arg):
|
||||
"""
|
||||
|
@ -2391,8 +2398,8 @@ class ChatControl(ChatControlBase):
|
|||
return gajim.nicks[self.account]
|
||||
|
||||
def print_conversation(self, text, frm='', tim=None, encrypted=False,
|
||||
subject=None, xhtml=None, simple=False, xep0184_id=None,
|
||||
displaymarking=None):
|
||||
subject=None, xhtml=None, simple=False, xep0184_id=None,
|
||||
displaymarking=None, msg_id=None):
|
||||
"""
|
||||
Print a line in the conversation
|
||||
|
||||
|
@ -2423,21 +2430,22 @@ class ChatControl(ChatControlBase):
|
|||
# ESessions
|
||||
if not encrypted:
|
||||
msg = _('The following message was NOT encrypted')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '',
|
||||
tim)
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status',
|
||||
'', tim)
|
||||
else:
|
||||
# GPG encryption
|
||||
if encrypted and not self.gpg_is_active:
|
||||
msg = _('The following message was encrypted')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '',
|
||||
tim)
|
||||
# turn on OpenPGP if this was in fact a XEP-0027 encrypted message
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status',
|
||||
'', tim)
|
||||
# turn on OpenPGP if this was in fact a XEP-0027 encrypted
|
||||
# message
|
||||
if encrypted == 'xep27':
|
||||
self._toggle_gpg()
|
||||
elif not encrypted and self.gpg_is_active:
|
||||
msg = _('The following message was NOT encrypted')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '',
|
||||
tim)
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status',
|
||||
'', tim)
|
||||
if not frm:
|
||||
kind = 'incoming'
|
||||
name = contact.get_shown_name()
|
||||
|
@ -2454,8 +2462,9 @@ class ChatControl(ChatControlBase):
|
|||
if xhtml:
|
||||
xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, xhtml)
|
||||
ChatControlBase.print_conversation_line(self, text, kind, name, tim,
|
||||
subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml,
|
||||
simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking)
|
||||
subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml,
|
||||
simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking,
|
||||
msg_id=msg_id)
|
||||
if text.startswith('/me ') or text.startswith('/me\n'):
|
||||
self.old_msg_kind = None
|
||||
else:
|
||||
|
@ -3238,6 +3247,36 @@ class ChatControl(ChatControlBase):
|
|||
b.connect('clicked', self._on_ok, file_props, type_)
|
||||
self._add_info_bar_message(markup, [b], file_props, gtk.MESSAGE_ERROR)
|
||||
|
||||
def _on_accept_gc_invitation(self, widget, event):
|
||||
room_jid = event.parameters[0]
|
||||
password = event.parameters[2]
|
||||
is_continued = event.parameters[3]
|
||||
try:
|
||||
if is_continued:
|
||||
gajim.interface.join_gc_room(self.account, room_jid,
|
||||
gajim.nicks[self.account], password, is_continued=True)
|
||||
else:
|
||||
dialogs.JoinGroupchatWindow(self.account, room_jid)
|
||||
except GajimGeneralException:
|
||||
pass
|
||||
gajim.events.remove_events(self.account, self.contact.jid, event=event)
|
||||
|
||||
def _on_cancel_gc_invitation(self, widget, event):
|
||||
gajim.events.remove_events(self.account, self.contact.jid, event=event)
|
||||
|
||||
def _get_gc_invitation(self, event):
|
||||
room_jid = event.parameters[0]
|
||||
comment = event.parameters[1]
|
||||
markup = '<b>%s:</b> %s' % (_('Groupchat Invitation'), room_jid)
|
||||
if comment:
|
||||
markup += ' (%s)' % comment
|
||||
b1 = gtk.Button(_('_Join'))
|
||||
b1.connect('clicked', self._on_accept_gc_invitation, event)
|
||||
b2 = gtk.Button(stock=gtk.STOCK_CANCEL)
|
||||
b2.connect('clicked', self._on_cancel_gc_invitation, event)
|
||||
self._add_info_bar_message(markup, [b1, b2], event.parameters,
|
||||
gtk.MESSAGE_QUESTION)
|
||||
|
||||
def on_event_added(self, event):
|
||||
if event.account != self.account:
|
||||
return
|
||||
|
@ -3259,6 +3298,8 @@ class ChatControl(ChatControlBase):
|
|||
self._got_file_error(event.parameters, event.type_,
|
||||
_('File transfer cancelled'),
|
||||
_('Connection with peer cannot be established.'))
|
||||
elif event.type_ == 'gc-invitation':
|
||||
self._get_gc_invitation(event)
|
||||
|
||||
def on_event_removed(self, event_list):
|
||||
"""
|
||||
|
@ -3270,14 +3311,24 @@ class ChatControl(ChatControlBase):
|
|||
if ev.jid != self.contact.jid:
|
||||
continue
|
||||
if ev.type_ not in ('file-request', 'file-completed', 'file-error',
|
||||
'file-stopped', 'file-request-error', 'file-send-error'):
|
||||
'file-stopped', 'file-request-error', 'file-send-error',
|
||||
'gc-invitation'):
|
||||
continue
|
||||
i = 0
|
||||
removed = False
|
||||
for ib_msg in self.info_bar_queue:
|
||||
if ib_msg[2] == ev.parameters:
|
||||
self.info_bar_queue.remove(ib_msg)
|
||||
if ev.type_ == 'gc-invitation':
|
||||
if ev.parameters[0] == ib_msg[2][0]:
|
||||
self.info_bar_queue.remove(ib_msg)
|
||||
removed = True
|
||||
else: # file-*
|
||||
if ib_msg[2] == ev.parameters:
|
||||
self.info_bar_queue.remove(ib_msg)
|
||||
removed = True
|
||||
if removed:
|
||||
if i == 0:
|
||||
# We are removing the one currently displayed
|
||||
self.info_bar.set_no_show_all(True)
|
||||
self.info_bar.hide()
|
||||
# show next one?
|
||||
gobject.idle_add(self._info_bar_show_message)
|
||||
|
|
|
@ -127,7 +127,7 @@ class StandardCommonCommands(CommandContainer):
|
|||
|
||||
@command(raw=True, empty=True)
|
||||
@doc(_("""
|
||||
Set current the status
|
||||
Set the current the status
|
||||
|
||||
Status can be given as one of the following values: online, away,
|
||||
chat, xa, dnd.
|
||||
|
@ -136,6 +136,11 @@ class StandardCommonCommands(CommandContainer):
|
|||
if status not in ('online', 'away', 'chat', 'xa', 'dnd'):
|
||||
raise CommandError("Invalid status given")
|
||||
for connection in gajim.connections.itervalues():
|
||||
if not gajim.config.get_per('accounts', connection.name,
|
||||
'sync_with_global_status'):
|
||||
continue
|
||||
if connection.connected < 2:
|
||||
continue
|
||||
connection.change_status(status, message)
|
||||
|
||||
@command(raw=True, empty=True)
|
||||
|
@ -143,7 +148,13 @@ class StandardCommonCommands(CommandContainer):
|
|||
def away(self, message):
|
||||
if not message:
|
||||
message = _("Away")
|
||||
|
||||
for connection in gajim.connections.itervalues():
|
||||
if not gajim.config.get_per('accounts', connection.name,
|
||||
'sync_with_global_status'):
|
||||
continue
|
||||
if connection.connected < 2:
|
||||
continue
|
||||
connection.change_status('away', message)
|
||||
|
||||
@command('back', raw=True, empty=True)
|
||||
|
@ -151,7 +162,13 @@ class StandardCommonCommands(CommandContainer):
|
|||
def online(self, message):
|
||||
if not message:
|
||||
message = _("Available")
|
||||
|
||||
for connection in gajim.connections.itervalues():
|
||||
if not gajim.config.get_per('accounts', connection.name,
|
||||
'sync_with_global_status'):
|
||||
continue
|
||||
if connection.connected < 2:
|
||||
continue
|
||||
connection.change_status('online', message)
|
||||
|
||||
class StandardCommonChatCommands(CommandContainer):
|
||||
|
@ -213,6 +230,11 @@ class StandardCommonChatCommands(CommandContainer):
|
|||
state = self._video_button.get_active()
|
||||
self._video_button.set_active(not state)
|
||||
|
||||
@command(raw=True)
|
||||
@doc(_("Send a message to the contact that will attract his (her) attention"))
|
||||
def attention(self, message):
|
||||
self.send_message(message, process_commands=False, attention=True)
|
||||
|
||||
class StandardChatCommands(CommandContainer):
|
||||
"""
|
||||
This command container contains standard commands which are unique
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||
## Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
## Copyright (C) 2008-2009 Stephan Erb <steve-e AT h3c.de>
|
||||
|
|
178
src/common/check_X509.py
Normal file
178
src/common/check_X509.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
import logging
|
||||
log = logging.getLogger('gajim.c.check_X509')
|
||||
|
||||
try:
|
||||
import OpenSSL.SSL
|
||||
import OpenSSL.crypto
|
||||
ver = OpenSSL.__version__
|
||||
if ver < '0.12':
|
||||
raise ImportError
|
||||
from pyasn1.type import univ, constraint, char, namedtype, tag
|
||||
from pyasn1.codec.der.decoder import decode
|
||||
from common.helpers import prep, InvalidFormat
|
||||
|
||||
MAX = 64
|
||||
oid_xmppaddr = '(1, 3, 6, 1, 5, 5, 7, 8, 5)'
|
||||
oid_dnssrv = '(1, 3, 6, 1, 5, 5, 7, 8, 7)'
|
||||
|
||||
|
||||
|
||||
class DirectoryString(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType(
|
||||
'teletexString', char.TeletexString().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'printableString', char.PrintableString().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'universalString', char.UniversalString().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'utf8String', char.UTF8String().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'bmpString', char.BMPString().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'ia5String', char.IA5String().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
namedtype.NamedType(
|
||||
'gString', univ.OctetString().subtype(
|
||||
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
|
||||
)
|
||||
|
||||
class AttributeValue(DirectoryString):
|
||||
pass
|
||||
|
||||
class AttributeType(univ.ObjectIdentifier):
|
||||
pass
|
||||
|
||||
class AttributeTypeAndValue(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('type', AttributeType()),
|
||||
namedtype.NamedType('value', AttributeValue()),
|
||||
)
|
||||
|
||||
class RelativeDistinguishedName(univ.SetOf):
|
||||
componentType = AttributeTypeAndValue()
|
||||
|
||||
class RDNSequence(univ.SequenceOf):
|
||||
componentType = RelativeDistinguishedName()
|
||||
|
||||
class Name(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('', RDNSequence()),
|
||||
)
|
||||
|
||||
class GeneralName(univ.Choice):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('otherName', univ.Sequence().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatConstructed, 0x0))),
|
||||
namedtype.NamedType('rfc822Name', char.IA5String().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatSimple, 1))),
|
||||
namedtype.NamedType('dNSName', char.IA5String().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatSimple, 2))),
|
||||
namedtype.NamedType('x400Address', univ.Sequence().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatConstructed, 0x3))),
|
||||
namedtype.NamedType('directoryName', Name().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatConstructed, 0x4))),
|
||||
namedtype.NamedType('ediPartyName', univ.Sequence().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatConstructed, 0x5))),
|
||||
namedtype.NamedType('uniformResourceIdentifier',
|
||||
char.IA5String().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatSimple, 6))),
|
||||
namedtype.NamedType('iPAddress', univ.OctetString().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatSimple, 7))),
|
||||
namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(
|
||||
implicitTag=tag.Tag(tag.tagClassContext,
|
||||
tag.tagFormatSimple, 8))),
|
||||
)
|
||||
|
||||
class GeneralNames(univ.SequenceOf):
|
||||
componentType = GeneralName()
|
||||
sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
|
||||
|
||||
def _parse_asn1(asn1):
|
||||
obj = decode(asn1, asn1Spec=GeneralNames())[0]
|
||||
r = {}
|
||||
for o in obj:
|
||||
name = o.getName()
|
||||
if name == 'dNSName':
|
||||
if name not in r:
|
||||
r[name] = []
|
||||
r[name].append(str(o.getComponent()))
|
||||
if name == 'otherName':
|
||||
if name not in r:
|
||||
r[name] = {}
|
||||
tag = str(tuple(o.getComponent())[0])
|
||||
val = str(tuple(o.getComponent())[1])
|
||||
if tag not in r[name]:
|
||||
r[name][tag] = []
|
||||
r[name][tag].append(val)
|
||||
if name == 'uniformResourceIdentifier':
|
||||
r['uniformResourceIdentifier'] = True
|
||||
return r
|
||||
|
||||
def check_certificate(cert, domain):
|
||||
cnt = cert.get_extension_count()
|
||||
if '.' in domain:
|
||||
compared_domain = domain.split('.', 1)[1]
|
||||
else:
|
||||
compared_domain = ''
|
||||
srv_domain = '_xmpp-client.' + domain
|
||||
compared_srv_domain = '_xmpp-client.' + compared_domain
|
||||
for i in range(0, cnt):
|
||||
ext = cert.get_extension(i)
|
||||
if ext.get_short_name() == 'subjectAltName':
|
||||
r = _parse_asn1(ext.get_data())
|
||||
if 'otherName' in r:
|
||||
if oid_xmppaddr in r['otherName']:
|
||||
for host in r['otherName'][oid_xmppaddr]:
|
||||
try:
|
||||
host = prep(None, host, None)
|
||||
except InvalidFormat:
|
||||
continue
|
||||
if host == domain:
|
||||
return True
|
||||
if oid_dnssrv in r['otherName']:
|
||||
for host in r['otherName'][oid_dnssrv]:
|
||||
if host.startswith('_xmpp-client.*.'):
|
||||
if host.replace('*.', '', 1) == compared_srv_domain:
|
||||
return True
|
||||
continue
|
||||
if host == srv_domain:
|
||||
return True
|
||||
if 'dNSName' in r:
|
||||
for host in r['dNSName']:
|
||||
if host.startswith('*.'):
|
||||
if host[2:] == compared_domain:
|
||||
return True
|
||||
continue
|
||||
if host == domain:
|
||||
return True
|
||||
if r:
|
||||
return False
|
||||
break
|
||||
|
||||
subject = cert.get_subject()
|
||||
if subject.commonName == domain:
|
||||
return True
|
||||
return False
|
||||
except ImportError:
|
||||
log.warn('Import of PyOpenSSL or pyasn1 failed. Cannot correctly check '
|
||||
'SSL certificate')
|
||||
|
||||
def check_certificate(cert, domain):
|
||||
subject = cert.get_subject()
|
||||
if subject.commonName == domain:
|
||||
return True
|
||||
return False
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2005-2006 Travis Shirk <travis AT pobox.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/commands.py
|
||||
##
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||
|
@ -56,10 +56,10 @@ class AdHocCommand:
|
|||
assert status in ('executing', 'completed', 'canceled')
|
||||
|
||||
response = request.buildReply('result')
|
||||
cmd = response.addChild('command', namespace=xmpp.NS_COMMANDS, attrs={
|
||||
'sessionid': self.sessionid,
|
||||
'node': self.commandnode,
|
||||
'status': status})
|
||||
cmd = response.getTag('command', namespace=xmpp.NS_COMMANDS)
|
||||
cmd.setAttr('sessionid', self.sessionid)
|
||||
cmd.setAttr('node', self.commandnode)
|
||||
cmd.setAttr('status', status)
|
||||
if defaultaction is not None or actions is not None:
|
||||
if defaultaction is not None:
|
||||
assert defaultaction in ('cancel', 'execute', 'prev', 'next',
|
||||
|
@ -277,13 +277,17 @@ class ForwardMessagesCommand(AdHocCommand):
|
|||
def execute(self, request):
|
||||
account = self.connection.name
|
||||
# Forward messages
|
||||
events = gajim.events.get_events(account, types=['chat', 'normal'])
|
||||
events = gajim.events.get_events(account, types=['chat', 'normal',
|
||||
'printed_chat'])
|
||||
j, resource = gajim.get_room_and_nick_from_fjid(self.jid)
|
||||
for jid in events:
|
||||
for event in events[jid]:
|
||||
ev_typ = event.type_
|
||||
if ev_typ == 'printed_chat':
|
||||
ev_typ = 'chat'
|
||||
self.connection.send_message(j, event.parameters[0], '',
|
||||
type_=event.type_, subject=event.parameters[1],
|
||||
resource=resource, forward_from=jid, delayed=event.time_)
|
||||
type_=ev_typ, subject=event.parameters[1],
|
||||
resource=resource, forward_from=jid, delayed=event.time_)
|
||||
|
||||
# Inform other client of completion
|
||||
response, cmd = self.buildResponse(request, status = 'completed')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/config.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005 Stéphan Kochen <stephan AT kochen.nl>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
|
@ -65,7 +65,7 @@ class Config:
|
|||
DEFAULT_MAILAPP = 'mozilla-thunderbird -compose'
|
||||
DEFAULT_FILE_MANAGER = 'xffm'
|
||||
|
||||
__options = {
|
||||
__options = ({
|
||||
# name: [ type, default_value, help_string ]
|
||||
'verbose': [ opt_bool, False, '', True ],
|
||||
'autopopup': [ opt_bool, False ],
|
||||
|
@ -99,6 +99,15 @@ class Config:
|
|||
'statusmsgcolor': [ opt_color, '#4e9a06', _('Status message text color.'), True ],
|
||||
'markedmsgcolor': [ opt_color, '#ff8080', '', True ],
|
||||
'urlmsgcolor': [ opt_color, '#204a87', '', True ],
|
||||
'notif_signin_color': [ opt_color, '#32CD32', _('Contact signed in notification color.') ], # limegreen
|
||||
'notif_signout_color': [ opt_color, '#FF0000', _('Contact signout notification color') ], # red
|
||||
'notif_message_color': [ opt_color, '#1E90FF', _('New message/email notification color.') ], # dodgerblue
|
||||
'notif_ftrequest_color': [ opt_color, '#F0E68C', _('File transfer request notification color.') ], # khaki
|
||||
'notif_fterror_color': [ opt_color, '#B22222', _('File transfer error notification color.') ], # firebrick
|
||||
'notif_ftcomplete_color': [ opt_color, '#9ACD32', _('File transfer complete or stopped notification color.') ], # yellowgreen
|
||||
'notif_invite_color': [ opt_color, '#D2B48C', _('Groupchat invitation notification color') ], # tan1
|
||||
'notif_status_color': [ opt_color, '#D8BFD8', _('Status changed notification background color') ], # thistle2
|
||||
'notif_other_color': [ opt_color, '#FFFFFF', _('Other dialogs color.') ], # white
|
||||
'inmsgfont': [ opt_str, '', _('Incoming nickname font.'), True ],
|
||||
'outmsgfont': [ opt_str, '', _('Outgoing nickname font.'), True ],
|
||||
'inmsgtxtfont': [ opt_str, '', _('Incoming text font.'), True ],
|
||||
|
@ -169,7 +178,7 @@ class Config:
|
|||
'change_roster_title': [ opt_bool, True, _('Add * and [n] in roster title?')],
|
||||
'restore_lines': [opt_int, 4, _('How many lines to remember from previous conversation when a chat tab/window is reopened.')],
|
||||
'restore_timeout': [opt_int, 60, _('How many minutes should last lines from previous conversation last.')],
|
||||
'muc_restore_lines': [opt_int, 20, _('How many lines to request to server when entering a groupchat. -1 means no limit')],
|
||||
'muc_restore_lines': [opt_int, 20, _('How many lines to request from server when entering a groupchat. -1 means no limit')],
|
||||
'muc_restore_timeout': [opt_int, 60, _('How many minutes back to request logs when a entering a groupchat. -1 means no limit')],
|
||||
'muc_autorejoin_timeout': [opt_int, 1, _('How many seconds to wait before trying to autorejoin to a conference you are being disconnected from. Set to 0 to disable autorejoining.')],
|
||||
'muc_autorejoin_on_kick': [opt_bool, False, _('Should autorejoin be activated when we are being kicked from a conference?')],
|
||||
|
@ -291,7 +300,8 @@ class Config:
|
|||
'stun_server': [opt_str, '', _('STUN server to use when using jingle')],
|
||||
'show_affiliation_in_groupchat': [opt_bool, True, _('If True, Gajim will show affiliation of groupchat occupants by adding a colored square to the status icon')],
|
||||
'global_proxy': [opt_str, '', _('Proxy used for all outgoing connections if the account does not have a specific proxy configured')],
|
||||
}
|
||||
'ignore_incoming_attention': [opt_bool, False, _('If True, Gajim will ignore incoming attention requestd ("wizz").')],
|
||||
}, {})
|
||||
|
||||
__options_per_key = {
|
||||
'accounts': ({
|
||||
|
@ -386,7 +396,7 @@ class Config:
|
|||
'ft_send_local_ips': [ opt_bool, True, _('If enabled, Gajim will send your local IPs so your contact can connect to your machine to transfer files.')],
|
||||
'oauth2_refresh_token': [ opt_str, '', _('Latest token for Oauth2 authentication.')],
|
||||
'oauth2_client_id': [ opt_str, '0000000044077801', _('client_id for Oauth2 authentication.')],
|
||||
'oauth2_redirect_url': [ opt_str, 'http%3A%2F%2Fgajim.org%2Fmsnauth%2Findex.cgi', _('redirect_url for Oauth2 authentication.')],
|
||||
'oauth2_redirect_url': [ opt_str, 'https%3A%2F%2Fgajim.org%2Fmsnauth%2Findex.cgi', _('redirect_url for Oauth2 authentication.')],
|
||||
}, {}),
|
||||
'statusmsg': ({
|
||||
'message': [ opt_str, '' ],
|
||||
|
@ -456,7 +466,7 @@ class Config:
|
|||
'speller_language': [ opt_str, '', _('Language for which we want to check misspelled words')],
|
||||
}, {}),
|
||||
'plugins': ({
|
||||
'active': [opt_bool, False, _('State whether plugins should be activated on exit (this is saved on Gajim exit). This option SHOULD NOT be used to (de)activate plug-ins. Use GUI instead.')],
|
||||
'active': [opt_bool, False, _('State whether plugins should be activated on startup (this is saved on Gajim exit). This option SHOULD NOT be used to (de)activate plug-ins. Use GUI instead.')],
|
||||
},{}),
|
||||
}
|
||||
|
||||
|
@ -488,6 +498,7 @@ class Config:
|
|||
}
|
||||
|
||||
soundevents_default = {
|
||||
'attention_received': [True, 'attention.wav'],
|
||||
'first_message_received': [ True, 'message1.wav' ],
|
||||
'next_message_received_focused': [ True, 'message2.wav' ],
|
||||
'next_message_received_unfocused': [ True, 'message2.wav' ],
|
||||
|
@ -526,9 +537,9 @@ class Config:
|
|||
_('Tor'): ['socks5', 'localhost', 9050],
|
||||
}
|
||||
|
||||
def foreach(self, cb, data = None):
|
||||
for opt in self.__options:
|
||||
cb(data, opt, None, self.__options[opt])
|
||||
def foreach(self, cb, data=None):
|
||||
for opt in self.__options[1]:
|
||||
cb(data, opt, None, self.__options[1][opt])
|
||||
for opt in self.__options_per_key:
|
||||
cb(data, opt, None, None)
|
||||
dict_ = self.__options_per_key[opt][1]
|
||||
|
@ -542,7 +553,7 @@ class Config:
|
|||
Tree-like interface
|
||||
"""
|
||||
if node is None:
|
||||
for child, option in self.__options.iteritems():
|
||||
for child, option in self.__options[1].iteritems():
|
||||
yield (child, ), option
|
||||
for grandparent in self.__options_per_key:
|
||||
yield (grandparent, ), None
|
||||
|
@ -598,35 +609,44 @@ class Config:
|
|||
return None
|
||||
|
||||
def set(self, optname, value):
|
||||
if optname not in self.__options:
|
||||
if optname not in self.__options[1]:
|
||||
# raise RuntimeError, 'option %s does not exist' % optname
|
||||
return
|
||||
opt = self.__options[optname]
|
||||
value = self.is_valid(opt[OPT_TYPE], value)
|
||||
value = self.is_valid(self.__options[0][optname][OPT_TYPE], value)
|
||||
if value is None:
|
||||
# raise RuntimeError, 'value of %s cannot be None' % optname
|
||||
return
|
||||
|
||||
opt[OPT_VAL] = value
|
||||
self.__options[1][optname] = value
|
||||
|
||||
def get(self, optname = None):
|
||||
def get(self, optname=None):
|
||||
if not optname:
|
||||
return self.__options.keys()
|
||||
if optname not in self.__options:
|
||||
return self.__options[1].keys()
|
||||
if optname not in self.__options[1]:
|
||||
return None
|
||||
return self.__options[optname][OPT_VAL]
|
||||
return self.__options[1][optname]
|
||||
|
||||
def get_default(self, optname):
|
||||
if optname not in self.__options[0]:
|
||||
return None
|
||||
return self.__options[0][optname][OPT_VAL]
|
||||
|
||||
def get_type(self, optname):
|
||||
if optname not in self.__options[0]:
|
||||
return None
|
||||
return self.__options[0][optname][OPT_TYPE][0]
|
||||
|
||||
def get_desc(self, optname):
|
||||
if optname not in self.__options:
|
||||
if optname not in self.__options[0]:
|
||||
return None
|
||||
if len(self.__options[optname]) > OPT_DESC:
|
||||
return self.__options[optname][OPT_DESC]
|
||||
if len(self.__options[0][optname]) > OPT_DESC:
|
||||
return self.__options[0][optname][OPT_DESC]
|
||||
|
||||
def get_restart(self, optname):
|
||||
if optname not in self.__options:
|
||||
if optname not in self.__options[0]:
|
||||
return None
|
||||
if len(self.__options[optname]) > OPT_RESTART:
|
||||
return self.__options[optname][OPT_RESTART]
|
||||
if len(self.__options[0][optname]) > OPT_RESTART:
|
||||
return self.__options[0][optname][OPT_RESTART]
|
||||
|
||||
def add_per(self, typename, name): # per_group_of_option
|
||||
if typename not in self.__options_per_key:
|
||||
|
@ -637,7 +657,9 @@ class Config:
|
|||
if name in opt[1]:
|
||||
# we already have added group name before
|
||||
return 'you already have added %s before' % name
|
||||
opt[1][name] = copy.deepcopy(opt[0])
|
||||
opt[1][name] = {}
|
||||
for o in opt[0]:
|
||||
opt[1][name][o] = opt[0][o][OPT_VAL]
|
||||
|
||||
def del_per(self, typename, name, subname = None): # per_group_of_option
|
||||
if typename not in self.__options_per_key:
|
||||
|
@ -665,36 +687,50 @@ class Config:
|
|||
if subname not in obj:
|
||||
# raise RuntimeError, '%s is not a key of %s' % (subname, obj)
|
||||
return
|
||||
subobj = obj[subname]
|
||||
value = self.is_valid(subobj[OPT_TYPE], value)
|
||||
typ = self.__options_per_key[optname][0][subname][OPT_TYPE]
|
||||
value = self.is_valid(typ, value)
|
||||
if value is None:
|
||||
# raise RuntimeError, '%s of %s cannot be None' % optname
|
||||
return
|
||||
subobj[OPT_VAL] = value
|
||||
obj[subname] = value
|
||||
|
||||
def get_per(self, optname, key = None, subname = None): # per_group_of_option
|
||||
def get_per(self, optname, key=None, subname=None): # per_group_of_option
|
||||
if optname not in self.__options_per_key:
|
||||
return None
|
||||
dict_ = self.__options_per_key[optname][1]
|
||||
if not key:
|
||||
return dict_.keys()
|
||||
if key not in dict_:
|
||||
if optname in self.__options_per_key \
|
||||
and subname in self.__options_per_key[optname][0]:
|
||||
return self.__options_per_key \
|
||||
[optname][0][subname][1]
|
||||
if subname in self.__options_per_key[optname][0]:
|
||||
return self.__options_per_key[optname][0][subname][1]
|
||||
return None
|
||||
obj = dict_[key]
|
||||
if not subname:
|
||||
return obj
|
||||
if subname not in obj:
|
||||
return None
|
||||
return obj[subname][OPT_VAL]
|
||||
return obj[subname]
|
||||
|
||||
def get_desc_per(self, optname, key = None, subname = None):
|
||||
def get_default_per(self, optname, subname):
|
||||
if optname not in self.__options_per_key:
|
||||
return None
|
||||
dict_ = self.__options_per_key[optname][1]
|
||||
dict_ = self.__options_per_key[optname][0]
|
||||
if subname not in dict_:
|
||||
return None
|
||||
return dict_[subname][OPT_VAL]
|
||||
|
||||
def get_type_per(self, optname, subname):
|
||||
if optname not in self.__options_per_key:
|
||||
return None
|
||||
dict_ = self.__options_per_key[optname][0]
|
||||
if subname not in dict_:
|
||||
return None
|
||||
return dict_[subname][OPT_TYPE][0]
|
||||
|
||||
def get_desc_per(self, optname, key=None, subname=None):
|
||||
if optname not in self.__options_per_key:
|
||||
return None
|
||||
dict_ = self.__options_per_key[optname][0]
|
||||
if not key:
|
||||
return None
|
||||
if key not in dict_:
|
||||
|
@ -708,10 +744,10 @@ class Config:
|
|||
return obj[subname][OPT_DESC]
|
||||
return None
|
||||
|
||||
def get_restart_per(self, optname, key = None, subname = None):
|
||||
def get_restart_per(self, optname, key=None, subname=None):
|
||||
if optname not in self.__options_per_key:
|
||||
return False
|
||||
dict_ = self.__options_per_key[optname][1]
|
||||
dict_ = self.__options_per_key[optname][0]
|
||||
if not key:
|
||||
return False
|
||||
if key not in dict_:
|
||||
|
@ -738,8 +774,13 @@ class Config:
|
|||
|
||||
return (account not in no_log_for) and (jid not in no_log_for)
|
||||
|
||||
def _init_options(self):
|
||||
for opt in self.__options[0]:
|
||||
self.__options[1][opt] = self.__options[0][opt][OPT_VAL]
|
||||
|
||||
def __init__(self):
|
||||
#init default values
|
||||
self._init_options()
|
||||
for event in self.soundevents_default:
|
||||
default = self.soundevents_default[event]
|
||||
self.add_per('soundevents', event)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Junglecow J <junglecow AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/connection.py
|
||||
##
|
||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
|
||||
## Stéphan Kochen <stephan AT kochen.nl>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
|
@ -58,6 +58,7 @@ from common import gajim
|
|||
from common import gpg
|
||||
from common import passwords
|
||||
from common import exceptions
|
||||
from common import check_X509
|
||||
from connection_handlers import *
|
||||
|
||||
from xmpp import Smacks
|
||||
|
@ -98,6 +99,7 @@ ssl_error = {
|
|||
31: _("Authority and issuer serial number mismatch"),
|
||||
32: _("Key usage does not include certificate signing"),
|
||||
50: _("Application verification failure")
|
||||
#100 is for internal usage: host not correct
|
||||
}
|
||||
|
||||
class CommonConnection:
|
||||
|
@ -251,7 +253,7 @@ class CommonConnection:
|
|||
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
|
||||
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||
session=None, forward_from=None, form_node=None, label=None,
|
||||
original_message=None, delayed=None, callback=None):
|
||||
original_message=None, delayed=None, attention=False, callback=None):
|
||||
if not self.connection or self.connected < 2:
|
||||
return 1
|
||||
try:
|
||||
|
@ -302,7 +304,8 @@ class CommonConnection:
|
|||
msgtxt, original_message, fjid, resource,
|
||||
jid, xhtml, subject, chatstate, msg_id,
|
||||
label, forward_from, delayed, session,
|
||||
form_node, user_nick, keyID, callback)
|
||||
form_node, user_nick, keyID, attention,
|
||||
callback)
|
||||
gajim.nec.push_incoming_event(GPGTrustKeyEvent(None,
|
||||
conn=self, callback=_on_always_trust))
|
||||
else:
|
||||
|
@ -310,7 +313,7 @@ class CommonConnection:
|
|||
original_message, fjid, resource, jid, xhtml,
|
||||
subject, chatstate, msg_id, label, forward_from,
|
||||
delayed, session, form_node, user_nick, keyID,
|
||||
callback)
|
||||
attention, callback)
|
||||
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
|
||||
_on_encrypted, [])
|
||||
return
|
||||
|
@ -318,18 +321,18 @@ class CommonConnection:
|
|||
self._message_encrypted_cb(('', error), type_, msg, msgtxt,
|
||||
original_message, fjid, resource, jid, xhtml, subject,
|
||||
chatstate, msg_id, label, forward_from, delayed, session,
|
||||
form_node, user_nick, keyID, callback)
|
||||
form_node, user_nick, keyID, attention, callback)
|
||||
return
|
||||
|
||||
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
|
||||
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
||||
label, forward_from, delayed, session, form_node, user_nick,
|
||||
callback)
|
||||
attention, callback)
|
||||
|
||||
def _message_encrypted_cb(self, output, type_, msg, msgtxt,
|
||||
original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
|
||||
label, forward_from, delayed, session, form_node, user_nick, keyID,
|
||||
callback):
|
||||
attention, callback):
|
||||
msgenc, error = output
|
||||
|
||||
if msgenc and not error:
|
||||
|
@ -342,7 +345,7 @@ class CommonConnection:
|
|||
self._on_continue_message(type_, msg, msgtxt, original_message,
|
||||
fjid, resource, jid, xhtml, subject, msgenc, keyID,
|
||||
chatstate, msg_id, label, forward_from, delayed, session,
|
||||
form_node, user_nick, callback)
|
||||
form_node, user_nick, attention, callback)
|
||||
return
|
||||
# Encryption failed, do not send message
|
||||
tim = localtime()
|
||||
|
@ -351,7 +354,8 @@ class CommonConnection:
|
|||
|
||||
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
|
||||
resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
|
||||
label, forward_from, delayed, session, form_node, user_nick, callback):
|
||||
label, forward_from, delayed, session, form_node, user_nick, attention,
|
||||
callback):
|
||||
if type_ == 'chat':
|
||||
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
|
||||
xhtml=xhtml)
|
||||
|
@ -422,6 +426,10 @@ class CommonConnection:
|
|||
if session.enable_encryption:
|
||||
msg_iq = session.encrypt_stanza(msg_iq)
|
||||
|
||||
# XEP-0224
|
||||
if attention:
|
||||
msg_iq.setTag('attention', namespace=common.xmpp.NS_ATTENTION)
|
||||
|
||||
if callback:
|
||||
callback(jid, msg, keyID, forward_from, session, original_message,
|
||||
subject, type_, msg_iq, xhtml)
|
||||
|
@ -808,6 +816,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
if not (self.sm and self.sm.resumption):
|
||||
gajim.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
||||
show='offline'))
|
||||
else:
|
||||
self.sm.enabled = False
|
||||
gajim.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
||||
show='error'))
|
||||
self.disconnect()
|
||||
if gajim.config.get_per('accounts', self.name, 'autoreconnect'):
|
||||
self.connected = -1
|
||||
|
@ -1012,7 +1024,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
proxy = {}
|
||||
proxyptr = gajim.config.get_per('proxies', p)
|
||||
for key in proxyptr.keys():
|
||||
proxy[key] = proxyptr[key][1]
|
||||
proxy[key] = proxyptr[key]
|
||||
else:
|
||||
proxy = None
|
||||
use_srv = True
|
||||
|
@ -1276,9 +1288,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
except AttributeError:
|
||||
errnum = -1 # we don't have an errnum
|
||||
if errnum > 0 and str(errnum) not in gajim.config.get_per('accounts',
|
||||
self.name, 'ignore_ssl_errors'):
|
||||
text = _('The authenticity of the %s certificate could be invalid.') %\
|
||||
hostname
|
||||
self.name, 'ignore_ssl_errors').split():
|
||||
text = _('The authenticity of the %s certificate could be invalid.'
|
||||
) % hostname
|
||||
if errnum in ssl_error:
|
||||
text += _('\nSSL Error: <b>%s</b>') % ssl_error[errnum]
|
||||
else:
|
||||
|
@ -1290,7 +1302,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
certificate=con.Connection.ssl_certificate))
|
||||
return True
|
||||
if hasattr(con.Connection, 'ssl_fingerprint_sha1'):
|
||||
saved_fingerprint = gajim.config.get_per('accounts', self.name, 'ssl_fingerprint_sha1')
|
||||
saved_fingerprint = gajim.config.get_per('accounts', self.name,
|
||||
'ssl_fingerprint_sha1')
|
||||
if saved_fingerprint:
|
||||
# Check sha1 fingerprint
|
||||
if con.Connection.ssl_fingerprint_sha1 != saved_fingerprint:
|
||||
|
@ -1299,15 +1312,24 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
new_fingerprint=con.Connection.ssl_fingerprint_sha1))
|
||||
return True
|
||||
else:
|
||||
gajim.config.set_per('accounts', self.name, 'ssl_fingerprint_sha1',
|
||||
con.Connection.ssl_fingerprint_sha1)
|
||||
gajim.config.set_per('accounts', self.name,
|
||||
'ssl_fingerprint_sha1', con.Connection.ssl_fingerprint_sha1)
|
||||
if not check_X509.check_certificate(con.Connection.ssl_certificate,
|
||||
hostname) and '100' not in gajim.config.get_per('accounts',
|
||||
self.name, 'ignore_ssl_errors').split():
|
||||
txt = _('The authenticity of the %s certificate could be '
|
||||
'invalid.\nThe certificate does not cover this domain.') % \
|
||||
hostname
|
||||
gajim.nec.push_incoming_event(SSLErrorEvent(None, conn=self,
|
||||
error_text=txt, error_num=100,
|
||||
cert=con.Connection.ssl_cert_pem,
|
||||
fingerprint=con.Connection.ssl_fingerprint_sha1,
|
||||
certificate=con.Connection.ssl_certificate))
|
||||
return True
|
||||
|
||||
self._register_handlers(con, con_type)
|
||||
con.auth(
|
||||
user=name,
|
||||
password=self.password,
|
||||
resource=self.server_resource,
|
||||
sasl=1,
|
||||
on_auth=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:
|
||||
|
@ -1317,7 +1339,10 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
msg=_('Connection with account %s has been lost. Retry '
|
||||
'connecting.') % self.name))
|
||||
return
|
||||
name = gajim.config.get_per('accounts', self.name, 'name')
|
||||
if gajim.config.get_per('accounts', self.name, 'anonymous_auth'):
|
||||
name = None
|
||||
else:
|
||||
name = gajim.config.get_per('accounts', self.name, 'name')
|
||||
self._register_handlers(self.connection, 'ssl')
|
||||
self.connection.auth(name, self.password, self.server_resource, 1,
|
||||
self.__on_auth)
|
||||
|
@ -1372,7 +1397,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
gajim.nec.push_incoming_event(InformationEvent(None, conn=self,
|
||||
level='error', pri_txt=_('Authentication failed with "%s"') % \
|
||||
self._hostname, sec_txt=_('Please check your login and password'
|
||||
'for correctness.')))
|
||||
' for correctness.')))
|
||||
if self.on_connect_auth:
|
||||
self.on_connect_auth(None)
|
||||
self.on_connect_auth = None
|
||||
|
@ -1449,7 +1474,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
level='error', pri_txt=_('Error while removing privacy '
|
||||
'list'), sec_txt=_('Privacy list %s has not been removed. '
|
||||
'It is maybe active in one of your connected resources. '
|
||||
'Deactivate it and tryagain.') % privacy_list))
|
||||
'Deactivate it and try again.') % privacy_list))
|
||||
common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list,
|
||||
_on_del_privacy_list_result)
|
||||
|
||||
|
@ -1775,8 +1800,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
|
||||
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||
label=None, session=None, forward_from=None, form_node=None,
|
||||
original_message=None, delayed=None, callback=None, callback_args=[],
|
||||
now=False):
|
||||
original_message=None, delayed=None, attention=False, callback=None,
|
||||
callback_args=[], now=False):
|
||||
|
||||
def cb(jid, msg, keyID, forward_from, session, original_message,
|
||||
subject, type_, msg_iq, xhtml):
|
||||
|
@ -1794,7 +1819,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
chatstate=chatstate, msg_id=msg_id, resource=resource,
|
||||
user_nick=user_nick, xhtml=xhtml, label=label, session=session,
|
||||
forward_from=forward_from, form_node=form_node,
|
||||
original_message=original_message, delayed=delayed, callback=cb)
|
||||
original_message=original_message, delayed=delayed,
|
||||
attention=attention, callback=cb)
|
||||
|
||||
def _nec_message_outgoing(self, obj):
|
||||
if obj.account != self.name:
|
||||
|
@ -1819,7 +1845,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
|
||||
label=obj.label, session=obj.session, forward_from=obj.forward_from,
|
||||
form_node=obj.form_node, original_message=obj.original_message,
|
||||
delayed=obj.delayed, callback=cb)
|
||||
delayed=obj.delayed, attention=obj.attention, callback=cb)
|
||||
|
||||
def send_contacts(self, contacts, jid):
|
||||
"""
|
||||
|
@ -2289,7 +2315,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
room_id=resp.getTag('unique').getData()))
|
||||
self.connection.SendAndCallForResponse(iq, _on_response)
|
||||
|
||||
def join_gc(self, nick, room_jid, password, change_nick=False):
|
||||
def join_gc(self, nick, room_jid, password, change_nick=False,
|
||||
rejoin=False):
|
||||
# FIXME: This room JID needs to be normalized; see #1364
|
||||
if not gajim.account_is_connected(self.name):
|
||||
return
|
||||
|
@ -2333,7 +2360,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
|||
last_date = self.last_history_time[room_jid]
|
||||
if last_date == 0:
|
||||
last_date = time.time() - timeout
|
||||
else:
|
||||
elif not rejoin:
|
||||
last_date = min(last_date, time.time() - timeout)
|
||||
last_date = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(
|
||||
last_date))
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
@ -869,7 +869,7 @@ class ConnectionHandlersBase:
|
|||
elif obj.contact.show in statuss:
|
||||
obj.old_show = statuss.index(obj.contact.show)
|
||||
if (resources != [''] and (len(obj.contact_list) != 1 or \
|
||||
obj.contact_list[0].show != 'offline')) and \
|
||||
obj.contact_list[0].show not in ('not in roster', 'offline'))) and \
|
||||
not gajim.jid_is_transport(jid):
|
||||
# Another resource of an existing contact connected
|
||||
obj.old_show = 0
|
||||
|
@ -897,7 +897,13 @@ class ConnectionHandlersBase:
|
|||
obj.contact.show = obj.show
|
||||
obj.contact.status = obj.status
|
||||
obj.contact.priority = obj.prio
|
||||
obj.contact.keyID = obj.keyID
|
||||
attached_keys = gajim.config.get_per('accounts', account,
|
||||
'attached_gpg_keys').split()
|
||||
if jid in attached_keys:
|
||||
obj.contact.keyID = attached_keys[attached_keys.index(jid) + 1]
|
||||
else:
|
||||
# Do not override assigned key
|
||||
obj.contact.keyID = obj.keyID
|
||||
if obj.timestamp:
|
||||
obj.contact.last_status_time = obj.timestamp
|
||||
elif not gajim.block_signed_in_notifications[account]:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/connection_handlers_events.py
|
||||
##
|
||||
## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2010-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
|
@ -1017,15 +1017,20 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|||
return
|
||||
self.jid = gajim.get_jid_without_resource(self.fjid)
|
||||
|
||||
forward_tag = self.stanza.getTag('forwarded', namespace=xmpp.NS_FORWARD)
|
||||
carbon_marker = self.stanza.getTag('sent', namespace=xmpp.NS_CARBONS)
|
||||
if not carbon_marker:
|
||||
carbon_marker = self.stanza.getTag('received', namespace=xmpp.NS_CARBONS)
|
||||
# Be sure it comes from one of our resource, else ignore forward element
|
||||
if forward_tag and self.jid == gajim.get_jid_from_account(account):
|
||||
received_tag = forward_tag.getTag('received',
|
||||
namespace=xmpp.NS_CARBONS)
|
||||
sent_tag = forward_tag.getTag('sent', namespace=xmpp.NS_CARBONS)
|
||||
if received_tag:
|
||||
if carbon_marker and self.jid == gajim.get_jid_from_account(account):
|
||||
forward_tag = self.stanza.getTag('forwarded', namespace=xmpp.NS_FORWARD)
|
||||
if forward_tag:
|
||||
msg = forward_tag.getTag('message')
|
||||
self.stanza = xmpp.Message(node=msg)
|
||||
if carbon_marker.getName() == 'sent':
|
||||
to = self.stanza.getTo()
|
||||
self.stanza.setTo(self.stanza.getFrom())
|
||||
self.stanza.setFrom(to)
|
||||
self.sent = True
|
||||
try:
|
||||
self.get_jid_resource()
|
||||
except helpers.InvalidFormat:
|
||||
|
@ -1036,23 +1041,6 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|||
'has been ignored.')))
|
||||
return
|
||||
self.forwarded = True
|
||||
elif sent_tag:
|
||||
msg = forward_tag.getTag('message')
|
||||
self.stanza = xmpp.Message(node=msg)
|
||||
to = self.stanza.getTo()
|
||||
self.stanza.setTo(self.stanza.getFrom())
|
||||
self.stanza.setFrom(to)
|
||||
try:
|
||||
self.get_jid_resource()
|
||||
except helpers.InvalidFormat:
|
||||
gajim.nec.push_incoming_event(InformationEvent(None,
|
||||
conn=self.conn, level='error',
|
||||
pri_txt=_('Invalid Jabber ID'),
|
||||
sec_txt=_('A message from a non-valid JID arrived, it '
|
||||
'has been ignored.')))
|
||||
return
|
||||
self.forwarded = True
|
||||
self.sent = True
|
||||
|
||||
self.enc_tag = self.stanza.getTag('x', namespace=xmpp.NS_ENCRYPTED)
|
||||
|
||||
|
@ -1205,6 +1193,7 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|||
self.sent = self.msg_obj.sent
|
||||
self.popup = False
|
||||
self.msg_id = None # id in log database
|
||||
self.attention = False # XEP-0224
|
||||
|
||||
self.receipt_request_tag = self.stanza.getTag('request',
|
||||
namespace=xmpp.NS_RECEIPTS)
|
||||
|
@ -1219,6 +1208,9 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|||
if self.seclabel:
|
||||
self.displaymarking = self.seclabel.getTag('displaymarking')
|
||||
|
||||
if self.stanza.getTag('attention', namespace=xmpp.NS_ATTENTION):
|
||||
self.attention = True
|
||||
|
||||
self.form_node = self.stanza.getTag('x', namespace=xmpp.NS_DATA)
|
||||
|
||||
if gajim.config.get('ignore_incoming_xhtml'):
|
||||
|
@ -1293,6 +1285,8 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
|
|||
if self.status_code != []:
|
||||
gajim.nec.push_incoming_event(GcConfigChangedReceivedEvent(
|
||||
None, conn=self.conn, msg_event=self))
|
||||
if self.msg_obj.form_node:
|
||||
return True
|
||||
return
|
||||
|
||||
self.displaymarking = None
|
||||
|
@ -2123,7 +2117,19 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
|||
# we're online or chat
|
||||
self.do_popup = True
|
||||
|
||||
if self.first_unread and helpers.allow_sound_notification(
|
||||
if msg_obj.attention and not gajim.config.get(
|
||||
'ignore_incoming_attention'):
|
||||
self.popup_timeout = 0
|
||||
self.do_popup = True
|
||||
else:
|
||||
self.popup_timeout = gajim.config.get('notification_timeout')
|
||||
|
||||
if msg_obj.attention and not gajim.config.get(
|
||||
'ignore_incoming_attention') and gajim.config.get_per('soundevents',
|
||||
'attention_received', 'enabled'):
|
||||
self.sound_event = 'attention_received'
|
||||
self.do_sound = True
|
||||
elif self.first_unread and helpers.allow_sound_notification(
|
||||
self.conn.name, 'first_message_received'):
|
||||
self.do_sound = True
|
||||
elif not self.first_unread and self.control_focused and \
|
||||
|
@ -2236,6 +2242,8 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
|||
self.popup_image = gtkgui_helpers.get_path_to_generic_or_avatar(
|
||||
img_path, jid=self.jid, suffix=suffix)
|
||||
|
||||
self.popup_timeout = gajim.config.get('notification_timeout')
|
||||
|
||||
if event == 'status_change':
|
||||
self.popup_title = _('%(nick)s Changed Status') % \
|
||||
{'nick': gajim.get_name_from_jid(account, self.jid)}
|
||||
|
@ -2280,6 +2288,7 @@ class NotificationEvent(nec.NetworkIncomingEvent):
|
|||
self.popup_event_type = ''
|
||||
self.popup_msg_type = ''
|
||||
self.popup_image = ''
|
||||
self.popup_timeout = -1
|
||||
|
||||
self.do_command = False
|
||||
self.command = ''
|
||||
|
@ -2322,6 +2331,7 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
|
|||
self.now = False
|
||||
self.is_loggable = True
|
||||
self.control = None
|
||||
self.attention = False
|
||||
|
||||
def generate(self):
|
||||
return True
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
|
||||
## Tomasz Melcer <liori AT exroot.org>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
## src/common/dataforms.py
|
||||
##
|
||||
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
## Copyright (C) 2005 Andrew Sayman <lorien420 AT myrealbox.com>
|
||||
## Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Stefan Bethge <stefan AT lanpartei.de>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/defs.py
|
||||
##
|
||||
## Copyright (C) 2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
|
||||
## Tomasz Melcer <liori AT exroot.org>
|
||||
|
@ -27,12 +27,13 @@ docdir = '../'
|
|||
basedir = '../'
|
||||
localedir = '../po'
|
||||
|
||||
version = '0.15-beta3'
|
||||
version = '0.15'
|
||||
import subprocess
|
||||
try:
|
||||
node = subprocess.Popen('hg tip --template "{node|short}"', shell=True,
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
version += '-' + node
|
||||
if node:
|
||||
version += '-' + node
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
@ -27,6 +27,17 @@ These imports will not be delayed:
|
|||
import __builtin__
|
||||
_origimport = __import__
|
||||
|
||||
nothing = object()
|
||||
|
||||
try:
|
||||
_origimport(__builtin__.__name__, {}, {}, None, -1)
|
||||
except TypeError: # no level argument
|
||||
def _import(name, globals, locals, fromlist, level):
|
||||
"call _origimport with no level argument"
|
||||
return _origimport(name, globals, locals, fromlist)
|
||||
else:
|
||||
_import = _origimport
|
||||
|
||||
class _demandmod(object):
|
||||
"""module demand-loader and proxy"""
|
||||
def __init__(self, name, globals, locals):
|
||||
|
@ -50,7 +61,7 @@ class _demandmod(object):
|
|||
h, t = p, None
|
||||
if '.' in p:
|
||||
h, t = p.split('.', 1)
|
||||
if not hasattr(mod, h):
|
||||
if getattr(mod, h, nothing) is nothing:
|
||||
setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
|
||||
elif t:
|
||||
subload(getattr(mod, h), t)
|
||||
|
@ -78,20 +89,17 @@ class _demandmod(object):
|
|||
self._load()
|
||||
setattr(self._module, attr, val)
|
||||
|
||||
def _demandimport(name, globals=None, locals=None, fromlist=None, level=None):
|
||||
def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
|
||||
if not locals or name in ignore or fromlist == ('*',):
|
||||
# these cases we can't really delay
|
||||
if level is None:
|
||||
return _origimport(name, globals, locals, fromlist)
|
||||
else:
|
||||
return _origimport(name, globals, locals, fromlist, level)
|
||||
return _import(name, globals, locals, fromlist, level)
|
||||
elif not fromlist:
|
||||
# import a [as b]
|
||||
if '.' in name: # a.b
|
||||
base, rest = name.split('.', 1)
|
||||
# email.__init__ loading email.mime
|
||||
if globals and globals.get('__name__', None) == base:
|
||||
return _origimport(name, globals, locals, fromlist)
|
||||
return _import(name, globals, locals, fromlist, level)
|
||||
# if a is already demand-loaded, add b to its submodule list
|
||||
if base in locals:
|
||||
if isinstance(locals[base], _demandmod):
|
||||
|
@ -99,19 +107,19 @@ def _demandimport(name, globals=None, locals=None, fromlist=None, level=None):
|
|||
return locals[base]
|
||||
return _demandmod(name, globals, locals)
|
||||
else:
|
||||
if level is not None:
|
||||
if level != -1:
|
||||
# from . import b,c,d or from .a import b,c,d
|
||||
return _origimport(name, globals, locals, fromlist, level)
|
||||
# from a import b,c,d
|
||||
mod = _origimport(name, globals, locals)
|
||||
# recurse down the module chain
|
||||
for comp in name.split('.')[1:]:
|
||||
if not hasattr(mod, comp):
|
||||
if getattr(mod, comp, nothing) is nothing:
|
||||
setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
|
||||
mod = getattr(mod, comp)
|
||||
for x in fromlist:
|
||||
# set requested submodules for demand load
|
||||
if not hasattr(mod, x):
|
||||
if getattr(mod, x, nothing) is nothing:
|
||||
setattr(mod, x, _demandmod(x, mod.__dict__, locals))
|
||||
return mod
|
||||
|
||||
|
@ -134,6 +142,8 @@ ignore = [
|
|||
# raise ImportError if x not defined
|
||||
'__main__',
|
||||
'_ssl', # conditional imports in the stdlib, issue1964
|
||||
'rfc822',
|
||||
'mimetools',
|
||||
]
|
||||
|
||||
def enable():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/dh.py
|
||||
##
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
|
||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||
|
@ -45,7 +45,7 @@ class Event:
|
|||
where kind in error, incoming
|
||||
file-*: file_props
|
||||
gc_msg: None
|
||||
printed_chat: control
|
||||
printed_chat: [message, subject, control, msg_id]
|
||||
printed_*: None
|
||||
messages that are already printed in chat, but not read
|
||||
gc-invitation: [room_jid, reason, password, is_continued]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/exceptions.py
|
||||
##
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
|
||||
##
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/fuzzyclock.py
|
||||
##
|
||||
## Copyright (C) 2006 Christoph Neuroth <delmonico AT gmx.net>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2009 Benjamin Richter <br AT waldteufel-online.net>
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/gajim.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
|
@ -171,21 +171,21 @@ else:
|
|||
# read.
|
||||
HAVE_LATEX = False
|
||||
|
||||
HAVE_FARSIGHT = True
|
||||
HAVE_FARSTREAM = True
|
||||
try:
|
||||
farsight = __import__('farsight')
|
||||
farstream = __import__('farstream')
|
||||
import gst
|
||||
import glib
|
||||
try:
|
||||
conference = gst.element_factory_make('fsrtpconference')
|
||||
session = conference.new_session(farsight.MEDIA_TYPE_AUDIO)
|
||||
session = conference.new_session(farstream.MEDIA_TYPE_AUDIO)
|
||||
del session
|
||||
del conference
|
||||
except glib.GError:
|
||||
HAVE_FARSIGHT = False
|
||||
HAVE_FARSTREAM = False
|
||||
|
||||
except ImportError:
|
||||
HAVE_FARSIGHT = False
|
||||
HAVE_FARSTREAM = False
|
||||
|
||||
HAVE_UPNP_IGD = True
|
||||
try:
|
||||
|
|
|
@ -27,6 +27,7 @@ Global Events Dispatcher module.
|
|||
|
||||
import traceback
|
||||
|
||||
from common.xmpp import NodeProcessed
|
||||
import logging
|
||||
log = logging.getLogger('gajim.c.ged')
|
||||
|
||||
|
@ -86,11 +87,16 @@ class GlobalEventsDispatcher(object):
|
|||
def raise_event(self, event_name, *args, **kwargs):
|
||||
log.debug('%s\nArgs: %s'%(event_name, str(args)))
|
||||
if event_name in self.handlers:
|
||||
node_processed = False
|
||||
for priority, handler in self.handlers[event_name]:
|
||||
try:
|
||||
if handler(*args, **kwargs):
|
||||
return True
|
||||
except NodeProcessed:
|
||||
node_processed = True
|
||||
except Exception, e:
|
||||
log.error('Error while running an even handler: %s' % \
|
||||
handler)
|
||||
traceback.print_exc()
|
||||
if node_processed:
|
||||
raise NodeProcessed
|
||||
|
|
|
@ -962,7 +962,7 @@ class Sign(object):
|
|||
|
||||
def handle_status(self, key, value):
|
||||
if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
|
||||
"GOOD_PASSPHRASE", "BEGIN_SIGNING"):
|
||||
"GOOD_PASSPHRASE", "BEGIN_SIGNING", "MISSING_PASSPHRASE"):
|
||||
pass
|
||||
elif key == "SIG_CREATED":
|
||||
(self.type,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## src/common/gpg.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/helpers.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Alex Mauer <hawke AT hawkesnest.net>
|
||||
|
@ -1330,7 +1330,7 @@ def update_optional_features(account = None):
|
|||
gajim.gajim_optional_features[a].append(xmpp.NS_ESESSION)
|
||||
if gajim.config.get_per('accounts', a, 'answer_receipts'):
|
||||
gajim.gajim_optional_features[a].append(xmpp.NS_RECEIPTS)
|
||||
if gajim.HAVE_FARSIGHT:
|
||||
if gajim.HAVE_FARSTREAM:
|
||||
gajim.gajim_optional_features[a].append(xmpp.NS_JINGLE)
|
||||
gajim.gajim_optional_features[a].append(xmpp.NS_JINGLE_RTP)
|
||||
gajim.gajim_optional_features[a].append(xmpp.NS_JINGLE_RTP_AUDIO)
|
||||
|
@ -1436,8 +1436,10 @@ def get_proxy_info(account):
|
|||
if p:
|
||||
proxy = {}
|
||||
proxyptr = gajim.config.get_per('proxies', p)
|
||||
if not proxyptr:
|
||||
return proxy
|
||||
for key in proxyptr.keys():
|
||||
proxy[key] = proxyptr[key][1]
|
||||
proxy[key] = proxyptr[key]
|
||||
return proxy
|
||||
|
||||
def _get_img_direct(attrs):
|
||||
|
@ -1546,4 +1548,4 @@ def download_image(account, attrs):
|
|||
proxy = get_proxy_info(account)
|
||||
if proxy and proxy['type'] in ('http', 'socks5'):
|
||||
return _get_img_proxy(attrs, proxy)
|
||||
return _get_img_direct(attrs)
|
||||
return _get_img_direct(attrs)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/i18n.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2004 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2009 Benjamin Richter <br AT waldteufel-online.net>
|
||||
|
|
|
@ -35,7 +35,7 @@ import helpers
|
|||
import gajim
|
||||
|
||||
from jingle_session import JingleSession, JingleStates
|
||||
if gajim.HAVE_FARSIGHT:
|
||||
if gajim.HAVE_FARSTREAM:
|
||||
from jingle_rtp import JingleAudio, JingleVideo
|
||||
from jingle_ft import JingleFileTransfer
|
||||
from jingle_transport import JingleTransportSocks5, JingleTransportIBB
|
||||
|
|
|
@ -21,7 +21,7 @@ import gobject
|
|||
import socket
|
||||
|
||||
import xmpp
|
||||
import farsight, gst
|
||||
import farstream, gst
|
||||
from glib import GError
|
||||
|
||||
import gajim
|
||||
|
@ -42,8 +42,8 @@ class JingleRTPContent(JingleContent):
|
|||
JingleContent.__init__(self, session, transport)
|
||||
self.media = media
|
||||
self._dtmf_running = False
|
||||
self.farsight_media = {'audio': farsight.MEDIA_TYPE_AUDIO,
|
||||
'video': farsight.MEDIA_TYPE_VIDEO}[media]
|
||||
self.farstream_media = {'audio': farstream.MEDIA_TYPE_AUDIO,
|
||||
'video': farstream.MEDIA_TYPE_VIDEO}[media]
|
||||
|
||||
self.pipeline = None
|
||||
self.src_bin = None
|
||||
|
@ -59,7 +59,7 @@ class JingleRTPContent(JingleContent):
|
|||
self.callbacks['session-terminate'] += [self.__stop]
|
||||
self.callbacks['session-terminate-sent'] += [self.__stop]
|
||||
|
||||
def setup_stream(self):
|
||||
def setup_stream(self, on_src_pad_added):
|
||||
# pipeline and bus
|
||||
self.pipeline = gst.Pipeline()
|
||||
bus = self.pipeline.get_bus()
|
||||
|
@ -68,13 +68,12 @@ class JingleRTPContent(JingleContent):
|
|||
|
||||
# conference
|
||||
self.conference = gst.element_factory_make('fsrtpconference')
|
||||
self.conference.set_property('sdes-cname', self.session.ourjid)
|
||||
self.pipeline.add(self.conference)
|
||||
self.funnel = None
|
||||
|
||||
self.p2psession = self.conference.new_session(self.farsight_media)
|
||||
self.p2psession = self.conference.new_session(self.farstream_media)
|
||||
|
||||
participant = self.conference.new_participant(self.session.peerjid)
|
||||
participant = self.conference.new_participant()
|
||||
# FIXME: Consider a workaround, here...
|
||||
# pidgin and telepathy-gabble don't follow the XEP, and it won't work
|
||||
# due to bad controlling-mode
|
||||
|
@ -93,7 +92,9 @@ class JingleRTPContent(JingleContent):
|
|||
params['stun-ip'] = ip
|
||||
|
||||
self.p2pstream = self.p2psession.new_stream(participant,
|
||||
farsight.DIRECTION_RECV, 'nice', params)
|
||||
farstream.DIRECTION_RECV)
|
||||
self.p2pstream.connect('src-pad-added', on_src_pad_added)
|
||||
self.p2pstream.set_transmitter('nice', params)
|
||||
|
||||
def is_ready(self):
|
||||
return (JingleContent.is_ready(self) and self.candidates_ready)
|
||||
|
@ -117,7 +118,7 @@ class JingleRTPContent(JingleContent):
|
|||
# FIXME: connectivity should not be etablished yet
|
||||
# Instead, it should be etablished after session-accept!
|
||||
if self.sent:
|
||||
self.p2pstream.set_remote_candidates(candidates)
|
||||
self.p2pstream.add_remote_candidates(candidates)
|
||||
|
||||
def batch_dtmf(self, events):
|
||||
"""
|
||||
|
@ -140,15 +141,14 @@ class JingleRTPContent(JingleContent):
|
|||
|
||||
def _start_dtmf(self, event):
|
||||
if event in ('*', '#'):
|
||||
event = {'*': farsight.DTMF_EVENT_STAR,
|
||||
'#': farsight.DTMF_EVENT_POUND}[event]
|
||||
event = {'*': farstream.DTMF_EVENT_STAR,
|
||||
'#': farstream.DTMF_EVENT_POUND}[event]
|
||||
else:
|
||||
event = int(event)
|
||||
self.p2psession.start_telephony_event(event, 2,
|
||||
farsight.DTMF_METHOD_RTP_RFC4733)
|
||||
self.p2psession.start_telephony_event(event, 2)
|
||||
|
||||
def _stop_dtmf(self):
|
||||
self.p2psession.stop_telephony_event(farsight.DTMF_METHOD_RTP_RFC4733)
|
||||
self.p2psession.stop_telephony_event()
|
||||
|
||||
def _fill_content(self, content):
|
||||
content.addChild(xmpp.NS_JINGLE_RTP + ' description',
|
||||
|
@ -170,34 +170,33 @@ class JingleRTPContent(JingleContent):
|
|||
if message.type == gst.MESSAGE_ELEMENT:
|
||||
name = message.structure.get_name()
|
||||
log.debug('gst element message: %s: %s' % (name, message))
|
||||
if name == 'farsight-new-active-candidate-pair':
|
||||
if name == 'farstream-new-active-candidate-pair':
|
||||
pass
|
||||
elif name == 'farsight-recv-codecs-changed':
|
||||
elif name == 'farstream-recv-codecs-changed':
|
||||
pass
|
||||
elif name == 'farsight-codecs-changed':
|
||||
if self.sent and self.p2psession.get_property('codecs-ready'):
|
||||
elif name == 'farstream-codecs-changed':
|
||||
if self.sent and self.p2psession.get_property('codecs'):
|
||||
self.send_description_info()
|
||||
elif name == 'farsight-local-candidates-prepared':
|
||||
elif name == 'farstream-local-candidates-prepared':
|
||||
self.candidates_ready = True
|
||||
if self.is_ready():
|
||||
self.session.on_session_state_changed(self)
|
||||
elif name == 'farsight-new-local-candidate':
|
||||
elif name == 'farstream-new-local-candidate':
|
||||
candidate = message.structure['candidate']
|
||||
self.transport.candidates.append(candidate)
|
||||
if self.sent:
|
||||
# FIXME: Is this case even possible?
|
||||
self.send_candidate(candidate)
|
||||
elif name == 'farsight-component-state-changed':
|
||||
elif name == 'farstream-component-state-changed':
|
||||
state = message.structure['state']
|
||||
if state == farsight.STREAM_STATE_FAILED:
|
||||
if state == farstream.STREAM_STATE_FAILED:
|
||||
reason = xmpp.Node('reason')
|
||||
reason.setTag('failed-transport')
|
||||
self.session.remove_content(self.creator, self.name, reason)
|
||||
elif name == 'farsight-error':
|
||||
log.error('Farsight error #%d!\nMessage: %s\nDebug: %s' % (
|
||||
elif name == 'farstream-error':
|
||||
log.error('Farstream error #%d!\nMessage: %s' % (
|
||||
message.structure['error-no'],
|
||||
message.structure['error-msg'],
|
||||
message.structure['debug-msg']))
|
||||
message.structure['error-msg']))
|
||||
elif message.type == gst.MESSAGE_ERROR:
|
||||
# TODO: Fix it to fallback to videotestsrc anytime an error occur,
|
||||
# or raise an error, Jingle way
|
||||
|
@ -236,10 +235,10 @@ class JingleRTPContent(JingleContent):
|
|||
def on_negotiated(self):
|
||||
if self.accepted:
|
||||
if self.transport.remote_candidates:
|
||||
self.p2pstream.set_remote_candidates(self.transport.remote_candidates)
|
||||
self.p2pstream.add_remote_candidates(self.transport.remote_candidates)
|
||||
self.transport.remote_candidates = []
|
||||
# TODO: farsight.DIRECTION_BOTH only if senders='both'
|
||||
self.p2pstream.set_property('direction', farsight.DIRECTION_BOTH)
|
||||
# TODO: farstream.DIRECTION_BOTH only if senders='both'
|
||||
self.p2pstream.set_property('direction', farstream.DIRECTION_BOTH)
|
||||
JingleContent.on_negotiated(self)
|
||||
|
||||
def __on_remote_codecs(self, stanza, content, error, action):
|
||||
|
@ -252,8 +251,8 @@ class JingleRTPContent(JingleContent):
|
|||
if not codec['id'] or not codec['name'] or not codec['clockrate']:
|
||||
# ignore invalid payload-types
|
||||
continue
|
||||
c = farsight.Codec(int(codec['id']), codec['name'],
|
||||
self.farsight_media, int(codec['clockrate']))
|
||||
c = farstream.Codec(int(codec['id']), codec['name'],
|
||||
self.farstream_media, int(codec['clockrate']))
|
||||
if 'channels' in codec:
|
||||
c.channels = int(codec['channels'])
|
||||
else:
|
||||
|
@ -318,7 +317,7 @@ class JingleAudio(JingleRTPContent):
|
|||
self.out_volume.set_property('volume', vol)
|
||||
|
||||
def setup_stream(self):
|
||||
JingleRTPContent.setup_stream(self)
|
||||
JingleRTPContent.setup_stream(self, self._on_src_pad_added)
|
||||
|
||||
# Configure SPEEX
|
||||
# Workaround for psi (not needed since rev
|
||||
|
@ -326,10 +325,10 @@ class JingleAudio(JingleRTPContent):
|
|||
# place 16kHz before 8kHz, as buggy psi versions will take in
|
||||
# account only the first codec
|
||||
|
||||
codecs = [farsight.Codec(farsight.CODEC_ID_ANY, 'SPEEX',
|
||||
farsight.MEDIA_TYPE_AUDIO, 16000),
|
||||
farsight.Codec(farsight.CODEC_ID_ANY, 'SPEEX',
|
||||
farsight.MEDIA_TYPE_AUDIO, 8000)]
|
||||
codecs = [farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX',
|
||||
farstream.MEDIA_TYPE_AUDIO, 16000),
|
||||
farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX',
|
||||
farstream.MEDIA_TYPE_AUDIO, 8000)]
|
||||
self.p2psession.set_codec_preferences(codecs)
|
||||
|
||||
# the local parts
|
||||
|
@ -348,9 +347,8 @@ class JingleAudio(JingleRTPContent):
|
|||
|
||||
self.src_bin.get_pad('src').link(self.p2psession.get_property(
|
||||
'sink-pad'))
|
||||
self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
|
||||
|
||||
# The following is needed for farsight to process ICE requests:
|
||||
# The following is needed for farstream to process ICE requests:
|
||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
||||
|
||||
|
||||
|
@ -363,7 +361,7 @@ class JingleVideo(JingleRTPContent):
|
|||
# TODO: Everything is not working properly:
|
||||
# sometimes, one window won't show up,
|
||||
# sometimes it'll freeze...
|
||||
JingleRTPContent.setup_stream(self)
|
||||
JingleRTPContent.setup_stream(self, self._on_src_pad_added)
|
||||
|
||||
# the local parts
|
||||
if gajim.config.get('video_framerate'):
|
||||
|
@ -395,9 +393,8 @@ class JingleVideo(JingleRTPContent):
|
|||
|
||||
self.src_bin.get_pad('src').link(self.p2psession.get_property(
|
||||
'sink-pad'))
|
||||
self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
|
||||
|
||||
# The following is needed for farsight to process ICE requests:
|
||||
# The following is needed for farstream to process ICE requests:
|
||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
||||
|
||||
def get_fallback_src(self):
|
||||
|
|
|
@ -323,7 +323,7 @@ class JingleTransportIBB(JingleTransport):
|
|||
return transport
|
||||
|
||||
try:
|
||||
import farsight
|
||||
import farstream
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
@ -332,11 +332,11 @@ class JingleTransportICEUDP(JingleTransport):
|
|||
JingleTransport.__init__(self, TransportType.ICEUDP)
|
||||
|
||||
def make_candidate(self, candidate):
|
||||
types = {farsight.CANDIDATE_TYPE_HOST: 'host',
|
||||
farsight.CANDIDATE_TYPE_SRFLX: 'srflx',
|
||||
farsight.CANDIDATE_TYPE_PRFLX: 'prflx',
|
||||
farsight.CANDIDATE_TYPE_RELAY: 'relay',
|
||||
farsight.CANDIDATE_TYPE_MULTICAST: 'multicast'}
|
||||
types = {farstream.CANDIDATE_TYPE_HOST: 'host',
|
||||
farstream.CANDIDATE_TYPE_SRFLX: 'srflx',
|
||||
farstream.CANDIDATE_TYPE_PRFLX: 'prflx',
|
||||
farstream.CANDIDATE_TYPE_RELAY: 'relay',
|
||||
farstream.CANDIDATE_TYPE_MULTICAST: 'multicast'}
|
||||
attrs = {
|
||||
'component': candidate.component_id,
|
||||
'foundation': '1', # hack
|
||||
|
@ -348,7 +348,7 @@ class JingleTransportICEUDP(JingleTransport):
|
|||
}
|
||||
if candidate.type in types:
|
||||
attrs['type'] = types[candidate.type]
|
||||
if candidate.proto == farsight.NETWORK_PROTOCOL_UDP:
|
||||
if candidate.proto == farstream.NETWORK_PROTOCOL_UDP:
|
||||
attrs['protocol'] = 'udp'
|
||||
else:
|
||||
# we actually don't handle properly different tcp options in jingle
|
||||
|
@ -367,29 +367,29 @@ class JingleTransportICEUDP(JingleTransport):
|
|||
def parse_transport_stanza(self, transport):
|
||||
candidates = []
|
||||
for candidate in transport.iterTags('candidate'):
|
||||
cand = farsight.Candidate()
|
||||
cand = farstream.Candidate()
|
||||
cand.component_id = int(candidate['component'])
|
||||
cand.ip = str(candidate['ip'])
|
||||
cand.port = int(candidate['port'])
|
||||
cand.foundation = str(candidate['foundation'])
|
||||
#cand.type = farsight.CANDIDATE_TYPE_LOCAL
|
||||
#cand.type = farstream.CANDIDATE_TYPE_LOCAL
|
||||
cand.priority = int(candidate['priority'])
|
||||
|
||||
if candidate['protocol'] == 'udp':
|
||||
cand.proto = farsight.NETWORK_PROTOCOL_UDP
|
||||
cand.proto = farstream.NETWORK_PROTOCOL_UDP
|
||||
else:
|
||||
# we actually don't handle properly different tcp options in jingle
|
||||
cand.proto = farsight.NETWORK_PROTOCOL_TCP
|
||||
cand.proto = farstream.NETWORK_PROTOCOL_TCP
|
||||
|
||||
cand.username = str(transport['ufrag'])
|
||||
cand.password = str(transport['pwd'])
|
||||
|
||||
#FIXME: huh?
|
||||
types = {'host': farsight.CANDIDATE_TYPE_HOST,
|
||||
'srflx': farsight.CANDIDATE_TYPE_SRFLX,
|
||||
'prflx': farsight.CANDIDATE_TYPE_PRFLX,
|
||||
'relay': farsight.CANDIDATE_TYPE_RELAY,
|
||||
'multicast': farsight.CANDIDATE_TYPE_MULTICAST}
|
||||
types = {'host': farstream.CANDIDATE_TYPE_HOST,
|
||||
'srflx': farstream.CANDIDATE_TYPE_SRFLX,
|
||||
'prflx': farstream.CANDIDATE_TYPE_PRFLX,
|
||||
'relay': farstream.CANDIDATE_TYPE_RELAY,
|
||||
'multicast': farstream.CANDIDATE_TYPE_MULTICAST}
|
||||
if 'type' in candidate and candidate['type'] in types:
|
||||
cand.type = types[candidate['type']]
|
||||
else:
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
## Copyright (C) 2005-2006 Alex Mauer <hawke AT hawkesnest.net>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
|
@ -59,8 +59,21 @@ def check_blacklist(str_):
|
|||
|
||||
def get_tmpfile_name():
|
||||
random.seed()
|
||||
int_ = random.randint(0, 100)
|
||||
return os.path.join(gettempdir(), 'gajimtex_' + int_.__str__())
|
||||
nb = 0
|
||||
while(nb < 100):
|
||||
int_ = random.randint(0, 10000)
|
||||
filename = os.path.join(gettempdir(), 'gajimtex_' + int_.__str__())
|
||||
# Check if a file to not overwrite it
|
||||
ok = True
|
||||
extensions = ['.tex', '.log', '.aux', '.dvi']
|
||||
for ext in extensions:
|
||||
if os.path.exists(filename + ext):
|
||||
ok = False
|
||||
break
|
||||
if ok:
|
||||
return filename
|
||||
nb += 1
|
||||
return filename
|
||||
|
||||
def write_latex(filename, str_):
|
||||
texstr = '\\documentclass[12pt]{article}\\usepackage[dvips]{graphicx}'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
## src/common/location_listener.py
|
||||
##
|
||||
## Copyright (C) 2009-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2009-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/logger.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
|
@ -34,6 +34,7 @@ import time
|
|||
import datetime
|
||||
from gzip import GzipFile
|
||||
from cStringIO import StringIO
|
||||
import gobject
|
||||
|
||||
import exceptions
|
||||
import gajim
|
||||
|
@ -106,6 +107,7 @@ class Logger:
|
|||
def __init__(self):
|
||||
self.jids_already_in = [] # holds jids that we already have in DB
|
||||
self.con = None
|
||||
self.commit_timout_id = None
|
||||
|
||||
if not os.path.exists(LOG_DB_PATH):
|
||||
# this can happen only the first time (the time we create the db)
|
||||
|
@ -163,15 +165,25 @@ class Logger:
|
|||
self.open_db()
|
||||
self.get_jids_already_in_db()
|
||||
|
||||
def _really_commit(self):
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
self.commit_timout_id = None
|
||||
return False
|
||||
|
||||
def _timeout_commit(self):
|
||||
if self.commit_timout_id:
|
||||
return
|
||||
self.commit_timout_id = gobject.timeout_add(500, self._really_commit)
|
||||
|
||||
def simple_commit(self, sql_to_commit):
|
||||
"""
|
||||
Helper to commit
|
||||
"""
|
||||
self.cur.execute(sql_to_commit)
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
self._timeout_commit()
|
||||
|
||||
def get_jids_already_in_db(self):
|
||||
try:
|
||||
|
@ -398,12 +410,14 @@ class Logger:
|
|||
except sqlite.OperationalError, e:
|
||||
raise exceptions.PysqliteOperationalError(str(e))
|
||||
message_id = None
|
||||
try:
|
||||
self.con.commit()
|
||||
if write_unread:
|
||||
if write_unread:
|
||||
try:
|
||||
self.con.commit()
|
||||
message_id = self.cur.lastrowid
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
else:
|
||||
self._timeout_commit()
|
||||
if message_id:
|
||||
self.insert_unread_events(message_id, values[0])
|
||||
return message_id
|
||||
|
@ -922,10 +936,7 @@ class Logger:
|
|||
VALUES (?, ?, ?, ?);
|
||||
''', (hash_method, hash_, buffer(data), int(time.time())))
|
||||
# (1) -- note above
|
||||
try:
|
||||
self.con.commit()
|
||||
except sqlite.OperationalError, e:
|
||||
print >> sys.stderr, str(e)
|
||||
self._timeout_commit()
|
||||
|
||||
def update_caps_time(self, method, hash_):
|
||||
sql = '''UPDATE caps_cache SET last_seen = %d
|
||||
|
@ -963,9 +974,9 @@ class Logger:
|
|||
# Fill roster tables with the new roster
|
||||
for jid in roster:
|
||||
self.add_or_update_contact(account_jid, jid, roster[jid]['name'],
|
||||
roster[jid]['subscription'], roster[jid]['ask'],
|
||||
roster[jid]['groups'], commit=False)
|
||||
self.con.commit()
|
||||
roster[jid]['subscription'], roster[jid]['ask'],
|
||||
roster[jid]['groups'], commit=False)
|
||||
self._timeout_commit()
|
||||
|
||||
# At this point, we are sure the replacement works properly so we can
|
||||
# set the new roster_version value.
|
||||
|
@ -987,7 +998,7 @@ class Logger:
|
|||
self.cur.execute(
|
||||
'DELETE FROM roster_entry WHERE account_jid_id=? AND jid_id=?',
|
||||
(account_jid_id, jid_id))
|
||||
self.con.commit()
|
||||
self._timeout_commit()
|
||||
|
||||
def add_or_update_contact(self, account_jid, jid, name, sub, ask, groups,
|
||||
commit=True):
|
||||
|
@ -1022,7 +1033,7 @@ class Logger:
|
|||
self.convert_human_subscription_values_to_db_api_values(sub),
|
||||
bool(ask)))
|
||||
if commit:
|
||||
self.con.commit()
|
||||
self._timeout_commit()
|
||||
|
||||
def get_roster(self, account_jid):
|
||||
"""
|
||||
|
@ -1075,7 +1086,7 @@ class Logger:
|
|||
(account_jid_id,))
|
||||
self.cur.execute('DELETE FROM roster_group WHERE account_jid_id=?',
|
||||
(account_jid_id,))
|
||||
self.con.commit()
|
||||
self._timeout_commit()
|
||||
|
||||
def save_if_not_exists(self, with_, direction, tim, msg='', nick=None):
|
||||
if tim:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/optparser.py
|
||||
##
|
||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
@ -50,7 +50,7 @@ class OptionsParser:
|
|||
except Exception:
|
||||
if os.path.exists(self.__filename):
|
||||
#we talk about a file
|
||||
print _('error: cannot open %s for reading') % self.__filename
|
||||
print _('Error: cannot open %s for reading') % self.__filename
|
||||
return False
|
||||
|
||||
new_version = gajim.config.get('version')
|
||||
|
@ -85,7 +85,6 @@ class OptionsParser:
|
|||
def write_line(self, fd, opt, parents, value):
|
||||
if value is None:
|
||||
return
|
||||
value = value[1]
|
||||
# convert to utf8 before writing to file if needed
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode('utf-8')
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Gustavo J. A. M. Carneiro <gjcarneiro AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
|
||||
|
@ -63,7 +63,7 @@ class GnomePasswordStorage(PasswordStorage):
|
|||
def __init__(self):
|
||||
self.keyring = gnomekeyring.get_default_keyring_sync()
|
||||
if self.keyring is None:
|
||||
self.keyring = 'default'
|
||||
self.keyring = 'login'
|
||||
try:
|
||||
gnomekeyring.create_sync(self.keyring, None)
|
||||
except gnomekeyring.AlreadyExistsError:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/pep.py
|
||||
##
|
||||
## Copyright (C) 2007 Piotr Gaczkowski <doomhammerng AT gmail.com>
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Jonathan Schleifer <js-common.gajim AT webkeks.org>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/pubsub.py
|
||||
##
|
||||
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
|
||||
##
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2006 Santiago Gala
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/sleepy.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Mateusz Biliński <mateusz AT bilinski.it>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/common/stanza_session.py
|
||||
##
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
|
||||
## Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
@ -99,7 +99,7 @@ class StanzaSession(object):
|
|||
for event in gajim.events.get_events(self.conn.name, j, types=types):
|
||||
# the event wasn't in this session
|
||||
if (event.type_ == 'chat' and event.parameters[8] != self) or \
|
||||
(event.type_ == 'printed_chat' and event.parameters[0].session != \
|
||||
(event.type_ == 'printed_chat' and event.parameters[2].session != \
|
||||
self):
|
||||
continue
|
||||
|
||||
|
|
|
@ -577,8 +577,19 @@ class NonBlockingNonSASL(PlugIn):
|
|||
else:
|
||||
log.warn("Secure methods unsupported, performing plain text \
|
||||
authentication")
|
||||
query.setTagData('password', self.password)
|
||||
self._method = 'plain'
|
||||
self._owner._caller.get_password(self._on_password, self._method)
|
||||
return
|
||||
resp = self.owner.Dispatcher.SendAndWaitForResponse(iq,
|
||||
func=self._on_auth)
|
||||
|
||||
def _on_password(self, password):
|
||||
self.password = '' if password is None else password
|
||||
iq=Iq('set', NS_AUTH)
|
||||
query = iq.getTag('query')
|
||||
query.setTagData('username', self.user)
|
||||
query.setTagData('resource', self.resource)
|
||||
query.setTagData('password', self.password)
|
||||
resp = self.owner.Dispatcher.SendAndWaitForResponse(iq,
|
||||
func=self._on_auth)
|
||||
|
||||
|
|
|
@ -530,6 +530,7 @@ class NonBlockingClient:
|
|||
sm.set_owner(self)
|
||||
self.Dispatcher.sm = sm
|
||||
nb_bind.PlugIn(self)
|
||||
self.on_auth(self, 'sasl')
|
||||
return
|
||||
|
||||
nb_bind.PlugIn(self)
|
||||
|
|
|
@ -41,6 +41,7 @@ NS_ARCHIVE_MANAGE = NS_ARCHIVE + ':manage' # XEP-0136
|
|||
NS_ARCHIVE_MANUAL = NS_ARCHIVE + ':manual' # XEP-0136
|
||||
NS_ARCHIVE_PREF = NS_ARCHIVE + ':pref'
|
||||
NS_ATOM = 'http://www.w3.org/2005/Atom'
|
||||
NS_ATTENTION = 'urn:xmpp:attention:0' # XEP-0224
|
||||
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'
|
||||
|
|
|
@ -103,6 +103,7 @@ class Smacks():
|
|||
self.uqueue.pop(0)
|
||||
|
||||
if stanza.getName() == 'resumed':
|
||||
self.enabled = True
|
||||
self.resuming = True
|
||||
self.con.set_oldst()
|
||||
if self.uqueue != []:
|
||||
|
@ -114,6 +115,7 @@ class Smacks():
|
|||
# Ask for service discovery, etc..
|
||||
if stanza.getTag('item-not-found'):
|
||||
self.resuming = False
|
||||
self.enabled = False
|
||||
# we need to bind a resource
|
||||
self._owner.NonBlockingBind.resuming = False
|
||||
self._owner._on_auth_bind(None)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/common/xmpp/stringprepare.py
|
||||
##
|
||||
## Copyright (C) 2001-2005 Twisted Matrix Laboratories
|
||||
## Copyright (C) 2005-2011 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Stefan Bethge <stefan AT lanpartei.de>
|
||||
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
##
|
||||
|
|
|
@ -451,8 +451,6 @@ class NonBlockingTLS(PlugIn):
|
|||
try:
|
||||
self._owner.ssl_fingerprint_sha1 = cert.digest('sha1')
|
||||
self._owner.ssl_certificate = cert
|
||||
if errnum == 0:
|
||||
return True
|
||||
self._owner.ssl_errnum = errnum
|
||||
self._owner.ssl_cert_pem = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
|
|
|
@ -803,11 +803,19 @@ class ClientZeroconf:
|
|||
def on_ok(_waitid):
|
||||
# if timeout:
|
||||
# self._owner.set_timeout(timeout)
|
||||
to = stanza.getTo()
|
||||
to = unicode(stanza.getTo())
|
||||
to = gajim.get_jid_without_resource(to)
|
||||
|
||||
try:
|
||||
item = self.roster[to]
|
||||
except KeyError:
|
||||
# Contact offline
|
||||
item = None
|
||||
|
||||
conn = None
|
||||
if to in self.recipient_to_hash:
|
||||
conn = self.connections[self.recipient_to_hash[to]]
|
||||
elif item['address'] in self.ip_to_hash:
|
||||
elif item and item['address'] in self.ip_to_hash:
|
||||
hash_ = self.ip_to_hash[item['address']]
|
||||
if self.hash_to_port[hash_] == item['port']:
|
||||
conn = self.connections[hash_]
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
## - Travis Shirk <travis@pobox.com>
|
||||
## - Stefan Bethge <stefan@lanpartei.de>
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix@lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix@lagaule.org>
|
||||
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
|
||||
## Copyright (C) 2006 Nikos Kouremenos <nkour@jabber.org>
|
||||
## Dimitur Kirov <dkirov@gmail.com>
|
||||
|
@ -336,8 +336,8 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
|||
def send_message(self, jid, msg, keyID, type_='chat', subject='',
|
||||
chatstate=None, msg_id=None, resource=None, user_nick=None, xhtml=None,
|
||||
label=None, session=None, forward_from=None, form_node=None,
|
||||
original_message=None, delayed=None, callback=None, callback_args=[],
|
||||
now=True):
|
||||
original_message=None, delayed=None, attention=False, callback=None,
|
||||
callback_args=[], now=True):
|
||||
|
||||
def on_send_ok(msg_id):
|
||||
gajim.nec.push_incoming_event(MessageSentEvent(None, conn=self,
|
||||
|
@ -370,7 +370,8 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
|||
chatstate=chatstate, msg_id=msg_id, resource=resource,
|
||||
user_nick=user_nick, xhtml=xhtml, session=session,
|
||||
forward_from=forward_from, form_node=form_node,
|
||||
original_message=original_message, delayed=delayed, callback=cb)
|
||||
original_message=original_message, delayed=delayed,
|
||||
attention=attention, callback=cb)
|
||||
|
||||
def _nec_message_outgoing(self, obj):
|
||||
if obj.account != self.name:
|
||||
|
@ -411,7 +412,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
|||
resource=obj.resource, user_nick=obj.user_nick, xhtml=obj.xhtml,
|
||||
label=obj.label, session=obj.session, forward_from=obj.forward_from,
|
||||
form_node=obj.form_node, original_message=obj.original_message,
|
||||
delayed=obj.delayed, callback=cb)
|
||||
delayed=obj.delayed, attention=obj.attention, callback=cb)
|
||||
|
||||
def send_stanza(self, stanza):
|
||||
# send a stanza untouched
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/config.py
|
||||
##
|
||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
|
||||
## Stéphan Kochen <stephan AT kochen.nl>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
|
@ -1485,8 +1485,7 @@ class ManageProxiesWindow:
|
|||
gajim.config.del_per('proxies', old_name)
|
||||
gajim.config.add_per('proxies', new_name)
|
||||
for option in config:
|
||||
gajim.config.set_per('proxies', new_name, option,
|
||||
config[option][common.config.OPT_VAL])
|
||||
gajim.config.set_per('proxies', new_name, option, config[option])
|
||||
model.set_value(iter_, 0, new_name)
|
||||
|
||||
def on_proxytype_combobox_changed(self, widget):
|
||||
|
@ -2116,7 +2115,7 @@ class AccountsWindow:
|
|||
gajim.config.add_per('accounts', new_name)
|
||||
old_config = gajim.config.get_per('accounts', old_name)
|
||||
for opt in old_config:
|
||||
gajim.config.set_per('accounts', new_name, opt, old_config[opt][1])
|
||||
gajim.config.set_per('accounts', new_name, opt, old_config[opt])
|
||||
gajim.config.del_per('accounts', old_name)
|
||||
if self.current_account == old_name:
|
||||
self.current_account = new_name
|
||||
|
@ -3655,7 +3654,11 @@ class AccountCreationWizardWindow:
|
|||
password = self.xml.get_object('password_entry').get_text().decode(
|
||||
'utf-8')
|
||||
|
||||
jid = username + '@' + server
|
||||
if anonymous:
|
||||
jid = ''
|
||||
else:
|
||||
jid = username + '@'
|
||||
jid += server
|
||||
# check if jid is conform to RFC and stringprep it
|
||||
try:
|
||||
jid = helpers.parse_jid(jid)
|
||||
|
@ -3803,23 +3806,24 @@ class AccountCreationWizardWindow:
|
|||
self.back_button.show()
|
||||
self.forward_button.show()
|
||||
self.is_form = obj.is_form
|
||||
empty_config = True
|
||||
if obj.is_form:
|
||||
dataform = dataforms.ExtendForm(node=obj.config)
|
||||
self.data_form_widget = dataforms_widget.DataFormWidget(dataform)
|
||||
empty_config = False
|
||||
else:
|
||||
self.data_form_widget = FakeDataForm(obj.config)
|
||||
empty_config = True
|
||||
for field in obj.config:
|
||||
if field in ('key', 'instructions', 'x', 'registered'):
|
||||
continue
|
||||
empty_config = False
|
||||
break
|
||||
if empty_config:
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.notebook.set_current_page(4) # show form page
|
||||
return
|
||||
self.data_form_widget.show_all()
|
||||
self.xml.get_object('form_vbox').pack_start(self.data_form_widget)
|
||||
if empty_config:
|
||||
self.forward_button.set_sensitive(False)
|
||||
self.notebook.set_current_page(4) # show form page
|
||||
return
|
||||
self.ssl_fingerprint = obj.ssl_fingerprint
|
||||
self.ssl_cert = obj.ssl_cert
|
||||
if obj.ssl_msg:
|
||||
|
@ -4183,6 +4187,7 @@ class ManageSoundsWindow:
|
|||
# NOTE: sounds_ui_names MUST have all items of
|
||||
# sounds = gajim.config.get_per('soundevents') as keys
|
||||
sounds_dict = {
|
||||
'attention_received': _('Attention Message Received'),
|
||||
'first_message_received': _('First Message Received'),
|
||||
'next_message_received_focused': _('Next Message Received Focused'),
|
||||
'next_message_received_unfocused':
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
## Copyright (C) 2005-2006 Alex Mauer <hawke AT hawkesnest.net>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
|
@ -925,6 +925,9 @@ class ConversationTextview(gobject.GObject):
|
|||
childs[7].hide() # hide add to roster menuitem
|
||||
|
||||
if kind == 'xmpp':
|
||||
id_ = childs[0].connect('activate', self.on_copy_link_activate,
|
||||
'xmpp:' + text)
|
||||
self.handlers[id_] = childs[0]
|
||||
childs[2].hide() # copy mail address
|
||||
childs[3].hide() # open mail composer
|
||||
childs[4].hide() # jid section separator
|
||||
|
@ -934,7 +937,8 @@ class ConversationTextview(gobject.GObject):
|
|||
childs[6].hide() # join group chat
|
||||
childs[7].hide() # add to roster
|
||||
|
||||
childs[0].hide() # copy link location
|
||||
if kind != 'xmpp':
|
||||
childs[0].hide() # copy link location
|
||||
childs[1].hide() # open link in browser
|
||||
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/dataforms_widget.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
||||
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
##
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/dialogs.py
|
||||
##
|
||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
|
@ -233,7 +233,7 @@ class PassphraseDialog:
|
|||
cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
|
||||
|
||||
self.xml.connect_signals(self)
|
||||
self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.window.set_transient_for(gajim.interface.roster.window)
|
||||
self.window.show_all()
|
||||
|
||||
self.check = bool(checkbuttontext)
|
||||
|
@ -1936,6 +1936,9 @@ class CommonInputDialog:
|
|||
def on_cancelbutton_clicked(self, widget):
|
||||
self.dialog.destroy()
|
||||
|
||||
def destroy(self):
|
||||
self.dialog.destroy()
|
||||
|
||||
class InputDialog(CommonInputDialog):
|
||||
"""
|
||||
Class for Input dialog
|
||||
|
@ -2464,8 +2467,9 @@ class JoinGroupchatWindow:
|
|||
'groupchat.'))
|
||||
return
|
||||
nickname = self._nickname_entry.get_text().decode('utf-8')
|
||||
server = self.server_comboboxentry.child.get_text().decode('utf-8')
|
||||
room = self._room_jid_entry.get_text().decode('utf-8')
|
||||
server = self.server_comboboxentry.child.get_text().decode('utf-8').\
|
||||
strip()
|
||||
room = self._room_jid_entry.get_text().decode('utf-8').strip()
|
||||
room_jid = room + '@' + server
|
||||
password = self._password_entry.get_text().decode('utf-8')
|
||||
try:
|
||||
|
@ -2740,7 +2744,7 @@ class ChangePasswordDialog:
|
|||
|
||||
class PopupNotificationWindow:
|
||||
def __init__(self, event_type, jid, account, msg_type='',
|
||||
path_to_image=None, title=None, text=None):
|
||||
path_to_image=None, title=None, text=None, timeout=-1):
|
||||
self.account = account
|
||||
self.jid = jid
|
||||
self.msg_type = msg_type
|
||||
|
@ -2760,8 +2764,8 @@ class PopupNotificationWindow:
|
|||
title = ''
|
||||
|
||||
event_type_label.set_markup(
|
||||
'<span foreground="black" weight="bold">%s</span>' %
|
||||
gobject.markup_escape_text(title))
|
||||
'<span foreground="black" weight="bold">%s</span>' %
|
||||
gobject.markup_escape_text(title))
|
||||
|
||||
# set colors [ http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html ]
|
||||
self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('black'))
|
||||
|
@ -2771,25 +2775,25 @@ class PopupNotificationWindow:
|
|||
path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
|
||||
|
||||
if event_type == _('Contact Signed In'):
|
||||
bg_color = 'limegreen'
|
||||
bg_color = gajim.config.get('notif_signin_color')
|
||||
elif event_type == _('Contact Signed Out'):
|
||||
bg_color = 'red'
|
||||
bg_color = gajim.config.get('notif_signout_color')
|
||||
elif event_type in (_('New Message'), _('New Single Message'),
|
||||
_('New Private Message'), _('New E-mail')):
|
||||
bg_color = 'dodgerblue'
|
||||
bg_color = gajim.config.get('notif_message_color')
|
||||
elif event_type == _('File Transfer Request'):
|
||||
bg_color = 'khaki'
|
||||
bg_color = gajim.config.get('notif_ftrequest_color')
|
||||
elif event_type == _('File Transfer Error'):
|
||||
bg_color = 'firebrick'
|
||||
bg_color = gajim.config.get('notif_fterror_color')
|
||||
elif event_type in (_('File Transfer Completed'),
|
||||
_('File Transfer Stopped')):
|
||||
bg_color = 'yellowgreen'
|
||||
_('File Transfer Stopped')):
|
||||
bg_color = gajim.config.get('notif_ftcomplete_color')
|
||||
elif event_type == _('Groupchat Invitation'):
|
||||
bg_color = 'tan1'
|
||||
bg_color = gajim.config.get('notif_invite_color')
|
||||
elif event_type == _('Contact Changed Status'):
|
||||
bg_color = 'thistle2'
|
||||
bg_color = gajim.config.get('notif_status_color')
|
||||
else: # Unknown event! Shouldn't happen but deal with it
|
||||
bg_color = 'white'
|
||||
bg_color = gajim.config.get('notif_other_color')
|
||||
popup_bg_color = gtk.gdk.color_parse(bg_color)
|
||||
close_button.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
|
||||
eventbox.modify_bg(gtk.STATE_NORMAL, popup_bg_color)
|
||||
|
@ -2808,13 +2812,13 @@ class PopupNotificationWindow:
|
|||
pos_y = gajim.config.get('notification_position_y')
|
||||
if pos_y < 0:
|
||||
pos_y = gtk.gdk.screen_height() - \
|
||||
gajim.interface.roster.popups_notification_height + pos_y + 1
|
||||
gajim.interface.roster.popups_notification_height + pos_y + 1
|
||||
self.window.move(pos_x, pos_y)
|
||||
|
||||
xml.connect_signals(self)
|
||||
self.window.show_all()
|
||||
timeout = gajim.config.get('notification_timeout')
|
||||
gobject.timeout_add_seconds(timeout, self.on_timeout)
|
||||
if timeout > 0:
|
||||
gobject.timeout_add_seconds(timeout, self.on_timeout)
|
||||
|
||||
def on_close_button_clicked(self, widget):
|
||||
self.adjust_height_and_move_popup_notification_windows()
|
||||
|
@ -5154,6 +5158,7 @@ class VoIPCallReceivedDialog(object):
|
|||
session = gajim.connections[self.account].get_jingle_session(self.fjid,
|
||||
self.sid)
|
||||
if not session:
|
||||
dialog.destroy()
|
||||
return
|
||||
if response == gtk.RESPONSE_YES:
|
||||
#TODO: Ensure that ctrl.contact.resource == resource
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2005-2006 Stéphan Kochen <stephan AT kochen.nl>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
## Julien Pivotto <roidelapluie AT gmail.com>
|
||||
## Stefan Bethge <stefan AT lanpartei.de>
|
||||
## Stephan Erb <steve-e AT h3c.de>
|
||||
## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
@ -49,12 +49,12 @@ class FeaturesWindow:
|
|||
self.features = {
|
||||
_('SSL certificate validation'): (self.pyopenssl_available,
|
||||
_('A library used to validate server certificates to ensure a secure connection.'),
|
||||
_('Requires python-pyopenssl.'),
|
||||
_('Requires python-pyopenssl.')),
|
||||
_('Requires python-pyopenssl > 0.12 and pyasn1.'),
|
||||
_('Requires python-pyopenssl > 0.12 and pyasn1.')),
|
||||
_('Bonjour / Zeroconf'): (self.zeroconf_available,
|
||||
_('Serverless chatting with autodetected clients in a local network.'),
|
||||
_('Requires python-avahi.'),
|
||||
_('Requires pybonjour (http://o2s.csail.mit.edu/o2s-wiki/pybonjour).')),
|
||||
_('Requires pybonjour and bonjour SDK running (http://developer.apple.com/opensource/).')),
|
||||
_('Command line'): (self.dbus_available,
|
||||
_('A script to control Gajim via commandline.'),
|
||||
_('Requires python-dbus.'),
|
||||
|
@ -103,9 +103,9 @@ class FeaturesWindow:
|
|||
_('Generate XHTML output from RST code (see http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html).'),
|
||||
_('Requires python-docutils.'),
|
||||
_('Requires python-docutils.')),
|
||||
_('Audio / Video'): (self.farsight_available,
|
||||
_('Audio / Video'): (self.farstream_available,
|
||||
_('Ability to start audio and video chat.'),
|
||||
_('Requires python-farsight and gstreamer-plugins-bad.'),
|
||||
_('Requires python-farstream and gstreamer-plugins-bad.'),
|
||||
_('Feature not available under Windows.')),
|
||||
_('UPnP-IGD'): (self.gupnp_igd_available,
|
||||
_('Ability to request your router to forward port for file transfer.'),
|
||||
|
@ -169,6 +169,11 @@ class FeaturesWindow:
|
|||
try:
|
||||
import OpenSSL.SSL
|
||||
import OpenSSL.crypto
|
||||
ver = OpenSSL.__version__
|
||||
ver_l = [int(i) for i in ver.split('.')]
|
||||
if ver_l < [0, 12]:
|
||||
raise ImportError
|
||||
import pyasn1
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
@ -253,8 +258,8 @@ class FeaturesWindow:
|
|||
return False
|
||||
return True
|
||||
|
||||
def farsight_available(self):
|
||||
return gajim.HAVE_FARSIGHT
|
||||
def farstream_available(self):
|
||||
return gajim.HAVE_FARSTREAM
|
||||
|
||||
def gupnp_igd_available(self):
|
||||
return gajim.HAVE_UPNP_IGD
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/filetransfers_window.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
##
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Junglecow <junglecow AT gmail.com>
|
||||
## Travis Shirk <travis AT pobox.com>
|
||||
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
|
14
src/gajim.py
14
src/gajim.py
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/gajim.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
|
||||
## Norman Rasmussen <norman AT rasmussen.co.za>
|
||||
|
@ -233,12 +233,12 @@ else:
|
|||
elif sysname in ('FreeBSD', 'OpenBSD', 'NetBSD'):
|
||||
libc.setproctitle('gajim')
|
||||
|
||||
if gtk.pygtk_version < (2, 16, 0):
|
||||
pritext = _('Gajim needs PyGTK 2.16 or above')
|
||||
sectext = _('Gajim needs PyGTK 2.16 or above to run. Quiting...')
|
||||
elif gtk.gtk_version < (2, 16, 0):
|
||||
pritext = _('Gajim needs GTK 2.16 or above')
|
||||
sectext = _('Gajim needs GTK 2.16 or above to run. Quiting...')
|
||||
if gtk.pygtk_version < (2, 22, 0):
|
||||
pritext = _('Gajim needs PyGTK 2.22 or above')
|
||||
sectext = _('Gajim needs PyGTK 2.22 or above to run. Quiting...')
|
||||
elif gtk.gtk_version < (2, 22, 0):
|
||||
pritext = _('Gajim needs GTK 2.22 or above')
|
||||
sectext = _('Gajim needs GTK 2.22 or above to run. Quiting...')
|
||||
|
||||
from common import check_paths
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/gajim_themes_window.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/groupchat_control.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Alex Mauer <hawke AT hawkesnest.net>
|
||||
|
@ -212,7 +212,8 @@ class PrivateChatControl(ChatControl):
|
|||
self.parent_win.redraw_tab(self)
|
||||
self.update_ui()
|
||||
|
||||
def send_message(self, message, xhtml=None, process_commands=True):
|
||||
def send_message(self, message, xhtml=None, process_commands=True,
|
||||
attention=False):
|
||||
"""
|
||||
Call this method to send the message
|
||||
"""
|
||||
|
@ -237,7 +238,7 @@ class PrivateChatControl(ChatControl):
|
|||
return
|
||||
|
||||
ChatControl.send_message(self, message, xhtml=xhtml,
|
||||
process_commands=process_commands)
|
||||
process_commands=process_commands, attention=attention)
|
||||
|
||||
def update_ui(self):
|
||||
if self.contact.show == 'offline':
|
||||
|
@ -1274,7 +1275,7 @@ class GroupchatControl(ChatControlBase):
|
|||
gajim.gc_connected[obj.conn.name][self.room_jid]:
|
||||
return
|
||||
password = gajim.gc_passwords.get(self.room_jid, '')
|
||||
obj.conn.join_gc(self.nick, self.room_jid, password)
|
||||
obj.conn.join_gc(self.nick, self.room_jid, password, rejoin=True)
|
||||
|
||||
def _nec_decrypted_message_received(self, obj):
|
||||
if obj.conn.name != self.account:
|
||||
|
@ -1307,7 +1308,6 @@ class GroupchatControl(ChatControlBase):
|
|||
self._update_banner_state_image()
|
||||
if self.parent_win:
|
||||
self.parent_win.redraw_tab(self)
|
||||
gobject.idle_add(self.msg_textview.grab_focus)
|
||||
|
||||
def got_disconnected(self):
|
||||
self.list_treeview.set_model(None)
|
||||
|
@ -1351,7 +1351,7 @@ class GroupchatControl(ChatControlBase):
|
|||
return False
|
||||
password = gajim.gc_passwords.get(self.room_jid, '')
|
||||
gajim.connections[self.account].join_gc(self.nick, self.room_jid,
|
||||
password)
|
||||
password, rejoin=True)
|
||||
return True
|
||||
|
||||
def draw_roster(self):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/groups.py
|
||||
##
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
## src/gtkexcepthook.py
|
||||
##
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/gtkgui_helpers.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/gajim.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
|
||||
## Norman Rasmussen <norman AT rasmussen.co.za>
|
||||
|
@ -330,6 +330,16 @@ class Interface:
|
|||
if gc_control and gc_control.autorejoin:
|
||||
gc_control.autorejoin = False
|
||||
|
||||
def handle_event_gc_message(self, obj):
|
||||
if not obj.stanza.getTag('body'): # no <body>
|
||||
# It could be a voice request. See
|
||||
# http://www.xmpp.org/extensions/xep-0045.html#voiceapprove
|
||||
if obj.msg_obj.form_node:
|
||||
dialogs.SingleMessageWindow(obj.conn.name, obj.fjid,
|
||||
action='receive', from_whom=obj.fjid,
|
||||
subject='', message='', resource='', session=None,
|
||||
form_node=obj.msg_obj.form_node)
|
||||
|
||||
def handle_event_presence(self, obj):
|
||||
# 'NOTIFY' (account, (jid, status, status message, resource,
|
||||
# priority, # keyID, timestamp, contact_nickname))
|
||||
|
@ -1490,6 +1500,7 @@ class Interface:
|
|||
'fingerprint-error': [self.handle_event_fingerprint_error],
|
||||
'gc-invitation-received': [self.handle_event_gc_invitation],
|
||||
'gc-presence-received': [self.handle_event_gc_presence],
|
||||
'gc-message-received': [self.handle_event_gc_message],
|
||||
'gmail-notify': [self.handle_event_gmail_notify],
|
||||
'gpg-password-required': [self.handle_event_gpg_password_required],
|
||||
'gpg-trust-key': [self.handle_event_gpg_trust_key],
|
||||
|
@ -1625,7 +1636,7 @@ class Interface:
|
|||
return
|
||||
|
||||
if type_ == 'printed_chat':
|
||||
ctrl = event.parameters[0]
|
||||
ctrl = event.parameters[2]
|
||||
elif type_ == 'chat':
|
||||
session = event.parameters[8]
|
||||
ctrl = session.control
|
||||
|
@ -1663,7 +1674,7 @@ class Interface:
|
|||
event = gajim.events.get_first_event(account, jid, type_)
|
||||
|
||||
if type_ == 'printed_pm':
|
||||
ctrl = event.parameters[0]
|
||||
ctrl = event.parameters[2]
|
||||
elif type_ == 'pm':
|
||||
session = event.parameters[8]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/gui_menu_builder.py
|
||||
##
|
||||
## Copyright (C) 2009-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2009-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
##
|
||||
## This file is part of Gajim.
|
||||
##
|
||||
|
@ -272,8 +272,9 @@ control=None, gc_contact=None):
|
|||
|
||||
# Unsensitive many items when account is offline
|
||||
if gajim.account_is_disconnected(account):
|
||||
for widget in (start_chat_menuitem, rename_menuitem,
|
||||
edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem):
|
||||
for widget in (start_chat_menuitem, rename_menuitem,
|
||||
edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem,
|
||||
information_menuitem):
|
||||
widget.set_sensitive(False)
|
||||
|
||||
if not show_start_chat:
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
## src/history_window.py
|
||||
##
|
||||
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2003-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2005 Vincent Hanquez <tab AT snarc.org>
|
||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
## Copyright (C) 2005 Gustavo J. A. M. Carneiro
|
||||
## Copyright (C) 2006 Santiago Gala
|
||||
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
|
||||
## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2006-2012 Yann Leboulanger <asterix AT lagaule.org>
|
||||
## Copyright (C) 2007 Nikos Kouremenos <kourem AT gmail.com>
|
||||
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
|
||||
## Julien Pivotto <roidelapluie AT gmail.com>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue