merge zeroconf branch to trunk
This commit is contained in:
commit
1dbb2a891f
|
@ -2,6 +2,7 @@
|
||||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
<glade-interface>
|
<glade-interface>
|
||||||
|
|
||||||
<widget class="GtkWindow" id="accounts_window">
|
<widget class="GtkWindow" id="accounts_window">
|
||||||
<property name="border_width">12</property>
|
<property name="border_width">12</property>
|
||||||
<property name="title" translatable="yes">Accounts</property>
|
<property name="title" translatable="yes">Accounts</property>
|
||||||
|
@ -64,7 +65,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="tooltip" translatable="yes">If you have 2 or more accounts and it is checked, Gajim will list all contacts as if you had one account</property>
|
<property name="tooltip" translatable="yes">If you have 2 or more accounts and it is checked, Gajim will list all contacts as if you had one account</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="label" translatable="yes">_Merge accounts</property>
|
<property name="label" translatable="yes">Mer_ge accounts</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
<property name="focus_on_click">True</property>
|
<property name="focus_on_click">True</property>
|
||||||
|
@ -80,6 +81,38 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHSeparator" id="hseparator1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="enable_zeroconf_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="tooltip" translatable="yes">If checked, all local contacts that use a Bonjour compatible chat client (like iChat, Trillian or Gaim) will be shown in roster. You don't need to be connected to a jabber server for it to work.
|
||||||
|
This is only available if python-avahi is installed and avahi-daemon is running.</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">_Enable link-local messaging</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<widget class="GtkHButtonBox" id="hbuttonbox15">
|
<widget class="GtkHButtonBox" id="hbuttonbox15">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -267,4 +300,5 @@
|
||||||
</widget>
|
</widget>
|
||||||
</child>
|
</child>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
||||||
</glade-interface>
|
</glade-interface>
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkMenu" id="zeroconf_contact_context_menu">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="start_chat_menuitem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Start _Chat</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1534">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-jump-to</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="rename_menuitem">
|
||||||
|
<property name="label" translatable="yes">_Rename</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1535">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-refresh</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="edit_groups_menuitem">
|
||||||
|
<property name="label" translatable="yes">Edit _Groups</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkSeparatorMenuItem" id="above_send_file_separator">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="send_file_menuitem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Send _File</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1536">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-file</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
|
||||||
|
<property name="label" translatable="yes">Assign Open_PGP Key</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate" last_modification_time="Thu, 30 Jun 2005 22:57:59 GMT"/>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1537">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-dialog-authentication</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Add Special _Notification</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1538">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-info</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkSeparatorMenuItem" id="above_information_separator">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="information_menuitem">
|
||||||
|
<property name="label">gtk-info</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="history_menuitem">
|
||||||
|
<property name="label" translatable="yes">_History</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1539">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-justify-fill</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkMenu" id="zeroconf_context_menu">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="status_menuitem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">_Status</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1258">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-network</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="zeroconf_properties_menuitem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">_Modify Account...</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child internal-child="image">
|
||||||
|
<widget class="GtkImage" id="image1259">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="stock">gtk-preferences</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -0,0 +1,666 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkWindow" id="zeroconf_information_window">
|
||||||
|
<property name="border_width">12</property>
|
||||||
|
<property name="title">Contact Information</property>
|
||||||
|
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||||
|
<property name="modal">False</property>
|
||||||
|
<property name="resizable">False</property>
|
||||||
|
<property name="destroy_with_parent">False</property>
|
||||||
|
<property name="decorated">True</property>
|
||||||
|
<property name="skip_taskbar_hint">False</property>
|
||||||
|
<property name="skip_pager_hint">False</property>
|
||||||
|
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||||
|
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||||
|
<property name="focus_on_map">True</property>
|
||||||
|
<signal name="key_press_event" handler="on_zeroconf_information_window_key_press_event" last_modification_time="Fri, 29 Sep 2006 13:23:38 GMT"/>
|
||||||
|
<signal name="destroy" handler="on_zeroconf_information_window_destroy" last_modification_time="Fri, 29 Sep 2006 13:23:44 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="nickname_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkNotebook" id="information_notebook">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="show_tabs">True</property>
|
||||||
|
<property name="show_border">True</property>
|
||||||
|
<property name="tab_pos">GTK_POS_TOP</property>
|
||||||
|
<property name="scrollable">False</property>
|
||||||
|
<property name="enable_popup">False</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox3">
|
||||||
|
<property name="border_width">12</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="table7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="n_rows">4</property>
|
||||||
|
<property name="n_columns">2</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
|
<property name="column_spacing">12</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label51">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Local jid:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label53">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Resource:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label54">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Status:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="local_jid_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEventBox" id="resource_prio_label_eventbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="visible_window">False</property>
|
||||||
|
<property name="above_child">False</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="resource_prio_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEventBox" id="status_label_eventbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="visible_window">True</property>
|
||||||
|
<property name="above_child">False</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="status_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options">fill</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="log_history_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">_Log conversation history</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEventBox" id="PHOTO_eventbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="visible_window">False</property>
|
||||||
|
<property name="above_child">False</property>
|
||||||
|
<signal name="button_press_event" handler="on_PHOTO_eventbox_button_press_event" last_modification_time="Fri, 08 Sep 2006 21:34:18 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImage" id="PHOTO_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_expand">False</property>
|
||||||
|
<property name="tab_fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Contact</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="type">tab</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="table8">
|
||||||
|
<property name="border_width">16</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="n_rows">4</property>
|
||||||
|
<property name="n_columns">2</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="row_spacing">6</property>
|
||||||
|
<property name="column_spacing">12</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label55">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Jabber ID:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="jabber_id_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label56">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">E-Mail:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="email_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">True</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label58">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Last Name:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label59">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">First Name:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="first_name_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="y_options">expand</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="last_name_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">True</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0</property>
|
||||||
|
<property name="xpad">5</property>
|
||||||
|
<property name="ypad">5</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_expand">True</property>
|
||||||
|
<property name="tab_fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label57">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Personal</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="type">tab</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHButtonBox" id="hbuttonbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="close_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_default">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label">gtk-close</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_close_button_clicked" last_modification_time="Mon, 25 Sep 2006 05:08:55 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -0,0 +1,689 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkWindow" id="zeroconf_properties_window">
|
||||||
|
<property name="border_width">12</property>
|
||||||
|
<property name="title" translatable="yes">Modify Account</property>
|
||||||
|
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||||
|
<property name="modal">False</property>
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="destroy_with_parent">False</property>
|
||||||
|
<property name="decorated">True</property>
|
||||||
|
<property name="skip_taskbar_hint">False</property>
|
||||||
|
<property name="skip_pager_hint">False</property>
|
||||||
|
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||||
|
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||||
|
<property name="focus_on_map">True</property>
|
||||||
|
<signal name="destroy" handler="on_zeroconf_properties_window_destroy" last_modification_time="Tue, 19 Sep 2006 22:21:09 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox23">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkNotebook" id="notebook1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="show_tabs">True</property>
|
||||||
|
<property name="show_border">True</property>
|
||||||
|
<property name="tab_pos">GTK_POS_TOP</property>
|
||||||
|
<property name="scrollable">False</property>
|
||||||
|
<property name="enable_popup">False</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox38">
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="autoconnect_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="tooltip" translatable="yes">If checked, Gajim, when launched, will automatically connect to jabber using this account</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">C_onnect on Gajim startup</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="log_history_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Save conversation _logs for all contacts</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="sync_with_global_status_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="tooltip" translatable="yes">If checked, any change to the global status (handled by the combobox at the bottom of the roster window) will change the status of this account accordingly</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Synch_ronize account status with global status</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox24">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="custom_port_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="tooltip" translatable="yes">If the default port that is used for incoming messages is unfitting for your setup you can select another one here.
|
||||||
|
You might consider to change possible firewall settings.</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Use custom port:</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_custom_port_checkbutton_toggled" last_modification_time="Wed, 20 Sep 2006 18:29:49 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="custom_port_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">True</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
<property name="width_chars">6</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">10</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_expand">False</property>
|
||||||
|
<property name="tab_fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label7">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">General</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="type">tab</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTable" id="table1">
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="n_rows">6</property>
|
||||||
|
<property name="n_columns">2</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="row_spacing">5</property>
|
||||||
|
<property name="column_spacing">2</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="jabber_id_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">True</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label363">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Jabber ID:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label364">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">E-Mail:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="email_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">True</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label362">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Last Name:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="last_name_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">True</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">3</property>
|
||||||
|
<property name="bottom_attach">4</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label361">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">First Name:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">1</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="first_name_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">True</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">●</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
<property name="bottom_attach">3</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label366">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes"><b>Personal Information</b></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
<property name="bottom_attach">2</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
<property name="y_options"></property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox96">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label365">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes"><b>OpenPGP</b></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox25">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="gpg_key_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">No key selected</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="gpg_name_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="gpg_choose_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Choose _Key...</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_gpg_choose_button_clicked" last_modification_time="Mon, 02 Oct 2006 00:23:00 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox26">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkCheckButton" id="gpg_save_password_checkbutton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="tooltip" translatable="yes">If checked, Gajim will store the password in ~/.gajim/config with 'read' permission only for you</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Save _passphrase (insecure)</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<property name="active">False</property>
|
||||||
|
<property name="inconsistent">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_gpg_save_password_checkbutton_toggled" last_modification_time="Mon, 02 Oct 2006 00:27:36 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkEntry" id="gpg_password_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="visibility">False</property>
|
||||||
|
<property name="max_length">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<property name="has_frame">True</property>
|
||||||
|
<property name="invisible_char">*</property>
|
||||||
|
<property name="activates_default">False</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
<property name="bottom_attach">1</property>
|
||||||
|
<property name="x_options">fill</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="tab_expand">False</property>
|
||||||
|
<property name="tab_fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label360">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Personal Information</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="type">tab</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">2</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHButtonBox" id="hbuttonbox6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="cancel_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_default">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label">gtk-cancel</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Sun, 17 Apr 2005 18:17:10 GMT"/>
|
||||||
|
<accelerator key="Escape" modifiers="0" signal="clicked"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="save_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_default">True</property>
|
||||||
|
<property name="has_default">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label">gtk-save</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_save_button_clicked" last_modification_time="Mon, 28 Feb 2005 20:30:56 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -6,7 +6,8 @@
|
||||||
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
|
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
|
||||||
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
|
## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
|
||||||
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
|
## Copyright (C) 2005 Norman Rasmussen <norman@rasmussen.co.za>
|
||||||
##
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## This program is free software; you can redistribute it and/or modify
|
||||||
## it under the terms of the GNU General Public License as published
|
## it under the terms of the GNU General Public License as published
|
||||||
## by the Free Software Foundation; version 2 only.
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
@ -82,6 +83,7 @@ class Config:
|
||||||
'saveposition': [ opt_bool, True ],
|
'saveposition': [ opt_bool, True ],
|
||||||
'mergeaccounts': [ opt_bool, False, '', True ],
|
'mergeaccounts': [ opt_bool, False, '', True ],
|
||||||
'sort_by_show': [ opt_bool, True, '', True ],
|
'sort_by_show': [ opt_bool, True, '', True ],
|
||||||
|
'enable_zeroconf': [opt_bool, False, _('Enable link-local/zeroconf messaging')],
|
||||||
'use_speller': [ opt_bool, False, ],
|
'use_speller': [ opt_bool, False, ],
|
||||||
'speller_language': [ opt_str, '', _('Language used by speller')],
|
'speller_language': [ opt_str, '', _('Language used by speller')],
|
||||||
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
|
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
|
||||||
|
@ -259,6 +261,11 @@ class Config:
|
||||||
'msgwin-y-position': [opt_int, -1], # Default is to let the wm decide
|
'msgwin-y-position': [opt_int, -1], # Default is to let the wm decide
|
||||||
'msgwin-width': [opt_int, 480],
|
'msgwin-width': [opt_int, 480],
|
||||||
'msgwin-height': [opt_int, 440],
|
'msgwin-height': [opt_int, 440],
|
||||||
|
'is_zeroconf': [opt_bool, False],
|
||||||
|
'zeroconf_first_name': [ opt_str, '', '', True ],
|
||||||
|
'zeroconf_last_name': [ opt_str, '', '', True ],
|
||||||
|
'zeroconf_jabber_id': [ opt_str, '', '', True ],
|
||||||
|
'zeroconf_email': [ opt_str, '', '', True ],
|
||||||
}, {}),
|
}, {}),
|
||||||
'statusmsg': ({
|
'statusmsg': ({
|
||||||
'message': [ opt_str, '' ],
|
'message': [ opt_str, '' ],
|
||||||
|
|
|
@ -46,6 +46,7 @@ class Connection(ConnectionHandlers):
|
||||||
self.connection = None # xmpppy ClientCommon instance
|
self.connection = None # xmpppy ClientCommon instance
|
||||||
# this property is used to prevent double connections
|
# this property is used to prevent double connections
|
||||||
self.last_connection = None # last ClientCommon instance
|
self.last_connection = None # last ClientCommon instance
|
||||||
|
self.is_zeroconf = False
|
||||||
self.gpg = None
|
self.gpg = None
|
||||||
self.status = ''
|
self.status = ''
|
||||||
self.priority = gajim.get_priority(name, 'offline')
|
self.priority = gajim.get_priority(name, 'offline')
|
||||||
|
|
|
@ -120,6 +120,12 @@ status_before_autoaway = {}
|
||||||
SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
'invisible']
|
'invisible']
|
||||||
|
|
||||||
|
# zeroconf account name
|
||||||
|
ZEROCONF_ACC_NAME = 'Local'
|
||||||
|
priority_dict = {}
|
||||||
|
for status in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
|
||||||
|
priority_dict[status] = config.get('autopriority' + status)
|
||||||
|
|
||||||
def get_nick_from_jid(jid):
|
def get_nick_from_jid(jid):
|
||||||
pos = jid.find('@')
|
pos = jid.find('@')
|
||||||
return jid[:pos]
|
return jid[:pos]
|
||||||
|
|
|
@ -74,7 +74,6 @@ class Dispatcher(PlugIn):
|
||||||
self.RegisterProtocol('presence', Presence)
|
self.RegisterProtocol('presence', Presence)
|
||||||
self.RegisterProtocol('message', Message)
|
self.RegisterProtocol('message', Message)
|
||||||
self.RegisterDefaultHandler(self.returnStanzaHandler)
|
self.RegisterDefaultHandler(self.returnStanzaHandler)
|
||||||
# Register Gajim's event handler as soon as dispatcher begins
|
|
||||||
self.RegisterEventHandler(self._owner._caller._event_dispatcher)
|
self.RegisterEventHandler(self._owner._caller._event_dispatcher)
|
||||||
self.on_responses = {}
|
self.on_responses = {}
|
||||||
|
|
||||||
|
@ -84,7 +83,10 @@ class Dispatcher(PlugIn):
|
||||||
self._owner.lastErrNode = None
|
self._owner.lastErrNode = None
|
||||||
self._owner.lastErr = None
|
self._owner.lastErr = None
|
||||||
self._owner.lastErrCode = None
|
self._owner.lastErrCode = None
|
||||||
self.StreamInit()
|
if hasattr(self._owner, 'StreamInit'):
|
||||||
|
self._owner.StreamInit()
|
||||||
|
else:
|
||||||
|
self.StreamInit()
|
||||||
|
|
||||||
def plugout(self):
|
def plugout(self):
|
||||||
''' Prepares instance to be destructed. '''
|
''' Prepares instance to be destructed. '''
|
||||||
|
@ -134,7 +136,7 @@ class Dispatcher(PlugIn):
|
||||||
return 0
|
return 0
|
||||||
except ExpatError:
|
except ExpatError:
|
||||||
sys.exc_clear()
|
sys.exc_clear()
|
||||||
self.DEBUG('Invalid XML received from server. Forcing disconnect.')
|
self.DEBUG('Invalid XML received from server. Forcing disconnect.', 'error')
|
||||||
self._owner.Connection.pollend()
|
self._owner.Connection.pollend()
|
||||||
return 0
|
return 0
|
||||||
if len(self._pendingExceptions) > 0:
|
if len(self._pendingExceptions) > 0:
|
||||||
|
|
|
@ -183,7 +183,7 @@ class Session:
|
||||||
if self.sendbuffer:
|
if self.sendbuffer:
|
||||||
try:
|
try:
|
||||||
# LOCK_QUEUE
|
# LOCK_QUEUE
|
||||||
sent=self._send(self.sendbuffer) # âÌÏËÉÒÕÀÝÁÑ ÛÔÕÞËÁ!
|
sent=self._send(self.sendbuffer) # blocking socket
|
||||||
except:
|
except:
|
||||||
# UNLOCK_QUEUE
|
# UNLOCK_QUEUE
|
||||||
self.set_socket_state(SOCKET_DEAD)
|
self.set_socket_state(SOCKET_DEAD)
|
||||||
|
|
|
@ -0,0 +1,598 @@
|
||||||
|
## common/zeroconf/client_zeroconf.py
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
## 2006 Dimitur Kirov <dkirov@gmail.com>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
from common import gajim
|
||||||
|
import common.xmpp
|
||||||
|
from common.xmpp.idlequeue import IdleObject
|
||||||
|
from common.xmpp import dispatcher_nb, simplexml
|
||||||
|
from common.xmpp.client import *
|
||||||
|
from common.xmpp.simplexml import ustr
|
||||||
|
from common.zeroconf import zeroconf
|
||||||
|
|
||||||
|
from common.xmpp.protocol import *
|
||||||
|
import socket
|
||||||
|
import errno
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from common.zeroconf import roster_zeroconf
|
||||||
|
|
||||||
|
MAX_BUFF_LEN = 65536
|
||||||
|
DATA_RECEIVED='DATA RECEIVED'
|
||||||
|
DATA_SENT='DATA SENT'
|
||||||
|
TYPE_SERVER, TYPE_CLIENT = range(2)
|
||||||
|
|
||||||
|
# wait XX sec to establish a connection
|
||||||
|
CONNECT_TIMEOUT_SECONDS = 30
|
||||||
|
|
||||||
|
# after XX sec with no activity, close the stream
|
||||||
|
ACTIVITY_TIMEOUT_SECONDS = 180
|
||||||
|
|
||||||
|
class ZeroconfListener(IdleObject):
|
||||||
|
def __init__(self, port, conn_holder):
|
||||||
|
''' handle all incomming connections on ('0.0.0.0', port)'''
|
||||||
|
self.port = port
|
||||||
|
self.queue_idx = -1
|
||||||
|
#~ self.queue = None
|
||||||
|
self.started = False
|
||||||
|
self._sock = None
|
||||||
|
self.fd = -1
|
||||||
|
self.caller = conn_holder.caller
|
||||||
|
self.conn_holder = conn_holder
|
||||||
|
|
||||||
|
def bind(self):
|
||||||
|
self._serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||||
|
self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
# will fail when port is busy, or we don't have rights to bind
|
||||||
|
try:
|
||||||
|
self._serv.bind(('0.0.0.0', self.port))
|
||||||
|
except Exception, e:
|
||||||
|
# unable to bind, show error dialog
|
||||||
|
return None
|
||||||
|
self._serv.listen(socket.SOMAXCONN)
|
||||||
|
self._serv.setblocking(False)
|
||||||
|
self.fd = self._serv.fileno()
|
||||||
|
gajim.idlequeue.plug_idle(self, False, True)
|
||||||
|
self.started = True
|
||||||
|
|
||||||
|
def pollend(self):
|
||||||
|
''' called when we stop listening on (host, port) '''
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def pollin(self):
|
||||||
|
''' accept a new incomming connection and notify queue'''
|
||||||
|
sock = self.accept_conn()
|
||||||
|
P2PClient(sock[0], sock[1][0], sock[1][1], self.conn_holder)
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
''' free all resources, we are not listening anymore '''
|
||||||
|
gajim.idlequeue.remove_timeout(self.fd)
|
||||||
|
gajim.idlequeue.unplug_idle(self.fd)
|
||||||
|
self.fd = -1
|
||||||
|
self.started = False
|
||||||
|
try:
|
||||||
|
self._serv.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.conn_holder.kill_all_connections()
|
||||||
|
|
||||||
|
def accept_conn(self):
|
||||||
|
''' accepts a new incomming connection '''
|
||||||
|
_sock = self._serv.accept()
|
||||||
|
_sock[0].setblocking(False)
|
||||||
|
return _sock
|
||||||
|
|
||||||
|
class P2PClient(IdleObject):
|
||||||
|
def __init__(self, _sock, host, port, conn_holder, messagequeue = [], to = None):
|
||||||
|
self._owner = self
|
||||||
|
self.Namespace = 'jabber:client'
|
||||||
|
self.defaultNamespace = self.Namespace
|
||||||
|
self._component = 0
|
||||||
|
self._registered_name = None
|
||||||
|
self._caller = conn_holder.caller
|
||||||
|
self.conn_holder = conn_holder
|
||||||
|
self.messagequeue = messagequeue
|
||||||
|
self.to = to
|
||||||
|
self.Server = host
|
||||||
|
self.DBG = 'client'
|
||||||
|
self.Connection = None
|
||||||
|
if gajim.verbose:
|
||||||
|
debug = ['always', 'nodebuilder']
|
||||||
|
else:
|
||||||
|
debug = []
|
||||||
|
self._DEBUG = Debug.Debug(debug)
|
||||||
|
self.DEBUG = self._DEBUG.Show
|
||||||
|
self.debug_flags = self._DEBUG.debug_flags
|
||||||
|
self.debug_flags.append(self.DBG)
|
||||||
|
self.sock_hash = None
|
||||||
|
if _sock:
|
||||||
|
self.sock_type = TYPE_SERVER
|
||||||
|
else:
|
||||||
|
self.sock_type = TYPE_CLIENT
|
||||||
|
conn = P2PConnection('', _sock, host, port, self._caller, self.on_connect)
|
||||||
|
self.sock_hash = conn._sock.__hash__
|
||||||
|
self.conn_holder.add_connection(self, self.Server, self.to)
|
||||||
|
|
||||||
|
def add_message(self, message):
|
||||||
|
if self.Connection:
|
||||||
|
if self.Connection.state == -1:
|
||||||
|
return False
|
||||||
|
self.send(message)
|
||||||
|
else:
|
||||||
|
self.messagequeue.append(message)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_connect(self, conn):
|
||||||
|
self.Connection = conn
|
||||||
|
self.Connection.PlugIn(self)
|
||||||
|
dispatcher_nb.Dispatcher().PlugIn(self)
|
||||||
|
self._register_handlers()
|
||||||
|
if self.sock_type == TYPE_CLIENT:
|
||||||
|
while self.messagequeue:
|
||||||
|
message = self.messagequeue.pop(0)
|
||||||
|
self.send(message)
|
||||||
|
|
||||||
|
def StreamInit(self):
|
||||||
|
''' Send an initial stream header. '''
|
||||||
|
self.Dispatcher.Stream = simplexml.NodeBuilder()
|
||||||
|
self.Dispatcher.Stream._dispatch_depth = 2
|
||||||
|
self.Dispatcher.Stream.dispatch = self.Dispatcher.dispatch
|
||||||
|
self.Dispatcher.Stream.stream_header_received = self._check_stream_start
|
||||||
|
self.debug_flags.append(simplexml.DBG_NODEBUILDER)
|
||||||
|
self.Dispatcher.Stream.DEBUG = self.DEBUG
|
||||||
|
self.Dispatcher.Stream.features = None
|
||||||
|
if self.sock_type == TYPE_CLIENT:
|
||||||
|
self.send_stream_header()
|
||||||
|
|
||||||
|
def send_stream_header(self):
|
||||||
|
self.Dispatcher._metastream = Node('stream:stream')
|
||||||
|
self.Dispatcher._metastream.setNamespace(self.Namespace)
|
||||||
|
# XXX TLS support
|
||||||
|
#~ self._metastream.setAttr('version', '1.0')
|
||||||
|
self.Dispatcher._metastream.setAttr('xmlns:stream', NS_STREAMS)
|
||||||
|
self.Dispatcher.send("<?xml version='1.0'?>%s>" % str(self.Dispatcher._metastream)[:-2])
|
||||||
|
|
||||||
|
def _check_stream_start(self, ns, tag, attrs):
|
||||||
|
if ns<>NS_STREAMS or tag<>'stream':
|
||||||
|
self.Connection.DEBUG('Incorrect stream start: (%s,%s).Terminating! ' % (tag, ns), 'error')
|
||||||
|
self.Connection.disconnect()
|
||||||
|
return
|
||||||
|
if self.sock_type == TYPE_SERVER:
|
||||||
|
self.send_stream_header()
|
||||||
|
while self.messagequeue:
|
||||||
|
message = self.messagequeue.pop(0)
|
||||||
|
self.send(message)
|
||||||
|
|
||||||
|
|
||||||
|
def on_disconnect(self):
|
||||||
|
if self.conn_holder:
|
||||||
|
self.conn_holder.remove_connection(self.sock_hash)
|
||||||
|
if self.__dict__.has_key('Dispatcher'):
|
||||||
|
self.Dispatcher.PlugOut()
|
||||||
|
if self.__dict__.has_key('P2PConnection'):
|
||||||
|
self.P2PConnection.PlugOut()
|
||||||
|
self.Connection = None
|
||||||
|
self._caller = None
|
||||||
|
self.conn_holder = None
|
||||||
|
|
||||||
|
def force_disconnect(self):
|
||||||
|
if self.Connection:
|
||||||
|
self.disconnect()
|
||||||
|
else:
|
||||||
|
self.on_disconnect()
|
||||||
|
|
||||||
|
def _on_receive_document_attrs(self, data):
|
||||||
|
if data:
|
||||||
|
self.Dispatcher.ProcessNonBlocking(data)
|
||||||
|
if not hasattr(self, 'Dispatcher') or \
|
||||||
|
self.Dispatcher.Stream._document_attrs is None:
|
||||||
|
return
|
||||||
|
self.onreceive(None)
|
||||||
|
if self.Dispatcher.Stream._document_attrs.has_key('version') and \
|
||||||
|
self.Dispatcher.Stream._document_attrs['version'] == '1.0':
|
||||||
|
#~ self.onreceive(self._on_receive_stream_features)
|
||||||
|
#XXX continue with TLS
|
||||||
|
return
|
||||||
|
self.onreceive(None)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _register_handlers(self):
|
||||||
|
self.RegisterHandler('message', lambda conn, data:self._caller._messageCB(self.Server, conn, data))
|
||||||
|
self.RegisterHandler('iq', self._caller._siSetCB, 'set',
|
||||||
|
common.xmpp.NS_SI)
|
||||||
|
self.RegisterHandler('iq', self._caller._siErrorCB, 'error',
|
||||||
|
common.xmpp.NS_SI)
|
||||||
|
self.RegisterHandler('iq', self._caller._siResultCB, 'result',
|
||||||
|
common.xmpp.NS_SI)
|
||||||
|
self.RegisterHandler('iq', self._caller._bytestreamSetCB, 'set',
|
||||||
|
common.xmpp.NS_BYTESTREAM)
|
||||||
|
self.RegisterHandler('iq', self._caller._bytestreamResultCB, 'result',
|
||||||
|
common.xmpp.NS_BYTESTREAM)
|
||||||
|
self.RegisterHandler('iq', self._caller._bytestreamErrorCB, 'error',
|
||||||
|
common.xmpp.NS_BYTESTREAM)
|
||||||
|
|
||||||
|
class P2PConnection(IdleObject, PlugIn):
|
||||||
|
''' class for sending file to socket over socks5 '''
|
||||||
|
def __init__(self, sock_hash, _sock, host = None, port = None, caller = None, on_connect = None):
|
||||||
|
IdleObject.__init__(self)
|
||||||
|
self._owner = None
|
||||||
|
PlugIn.__init__(self)
|
||||||
|
self.DBG_LINE='socket'
|
||||||
|
self.sendqueue = []
|
||||||
|
self.sendbuff = None
|
||||||
|
self._sock = _sock
|
||||||
|
self.host, self.port = host, port
|
||||||
|
self.on_connect = on_connect
|
||||||
|
self.writable = False
|
||||||
|
self.readable = False
|
||||||
|
self._exported_methods=[self.send, self.disconnect, self.onreceive]
|
||||||
|
self.on_receive = None
|
||||||
|
if _sock:
|
||||||
|
self._sock = _sock
|
||||||
|
self.state = 1
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self.fd = self._sock.fileno()
|
||||||
|
self.on_connect(self)
|
||||||
|
else:
|
||||||
|
self.state = 0
|
||||||
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self.fd = self._sock.fileno()
|
||||||
|
gajim.idlequeue.plug_idle(self, True, False)
|
||||||
|
self.set_timeout(CONNECT_TIMEOUT_SECONDS)
|
||||||
|
self.do_connect()
|
||||||
|
|
||||||
|
def set_timeout(self, timeout):
|
||||||
|
gajim.idlequeue.remove_timeout(self.fd)
|
||||||
|
if self.state >= 0:
|
||||||
|
gajim.idlequeue.set_read_timeout(self.fd, timeout)
|
||||||
|
|
||||||
|
def plugin(self, owner):
|
||||||
|
self.onreceive(owner._on_receive_document_attrs)
|
||||||
|
self._plug_idle()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def plugout(self):
|
||||||
|
''' Disconnect from the remote server and unregister self.disconnected method from
|
||||||
|
the owner's dispatcher. '''
|
||||||
|
self.disconnect()
|
||||||
|
self._owner = None
|
||||||
|
|
||||||
|
def onreceive(self, recv_handler):
|
||||||
|
if not recv_handler:
|
||||||
|
if hasattr(self._owner, 'Dispatcher'):
|
||||||
|
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
|
||||||
|
else:
|
||||||
|
self.on_receive = None
|
||||||
|
return
|
||||||
|
_tmp = self.on_receive
|
||||||
|
# make sure this cb is not overriden by recursive calls
|
||||||
|
if not recv_handler(None) and _tmp == self.on_receive:
|
||||||
|
self.on_receive = recv_handler
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def send(self, stanza):
|
||||||
|
'''Append stanza to the queue of messages to be send.
|
||||||
|
If supplied data is unicode string, encode it to utf-8.
|
||||||
|
'''
|
||||||
|
if self.state <= 0:
|
||||||
|
return
|
||||||
|
r = stanza
|
||||||
|
if isinstance(r, unicode):
|
||||||
|
r = r.encode('utf-8')
|
||||||
|
elif not isinstance(r, str):
|
||||||
|
r = ustr(r).encode('utf-8')
|
||||||
|
self.sendqueue.append(r)
|
||||||
|
self._plug_idle()
|
||||||
|
|
||||||
|
def read_timeout(self):
|
||||||
|
self.pollend()
|
||||||
|
|
||||||
|
|
||||||
|
def do_connect(self):
|
||||||
|
errnum = 0
|
||||||
|
try:
|
||||||
|
self._sock.connect((self.host, self.port))
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
except Exception, ee:
|
||||||
|
(errnum, errstr) = ee
|
||||||
|
if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
|
||||||
|
return
|
||||||
|
# win32 needs this
|
||||||
|
elif errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
|
||||||
|
self.disconnect()
|
||||||
|
return None
|
||||||
|
else: # socket is already connected
|
||||||
|
self._sock.setblocking(False)
|
||||||
|
self.state = 1 # connected
|
||||||
|
self.on_connect(self)
|
||||||
|
return 1 # we are connected
|
||||||
|
|
||||||
|
|
||||||
|
def pollout(self):
|
||||||
|
if self.state == 0:
|
||||||
|
self.do_connect()
|
||||||
|
return
|
||||||
|
gajim.idlequeue.remove_timeout(self.fd)
|
||||||
|
self._do_send()
|
||||||
|
|
||||||
|
def pollend(self):
|
||||||
|
self.state = -1
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def pollin(self):
|
||||||
|
''' Reads all pending incoming data. Calls owner's disconnected() method if appropriate.'''
|
||||||
|
received = ''
|
||||||
|
errnum = 0
|
||||||
|
try:
|
||||||
|
# get as many bites, as possible, but not more than RECV_BUFSIZE
|
||||||
|
received = self._sock.recv(MAX_BUFF_LEN)
|
||||||
|
except Exception, e:
|
||||||
|
if len(e.args) > 0 and isinstance(e.args[0], int):
|
||||||
|
errnum = e[0]
|
||||||
|
sys.exc_clear()
|
||||||
|
# "received" will be empty anyhow
|
||||||
|
if errnum == socket.SSL_ERROR_WANT_READ:
|
||||||
|
pass
|
||||||
|
elif errnum in [errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN]:
|
||||||
|
self.pollend()
|
||||||
|
# don't proccess result, cas it will raise error
|
||||||
|
return
|
||||||
|
elif not received :
|
||||||
|
if errnum != socket.SSL_ERROR_EOF:
|
||||||
|
# 8 EOF occurred in violation of protocol
|
||||||
|
self.pollend()
|
||||||
|
if self.state >= 0:
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.state < 0:
|
||||||
|
return
|
||||||
|
if self.on_receive:
|
||||||
|
if self._owner.sock_type == TYPE_CLIENT:
|
||||||
|
self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
|
||||||
|
if received.strip():
|
||||||
|
self.DEBUG(received, 'got')
|
||||||
|
if hasattr(self._owner, 'Dispatcher'):
|
||||||
|
self._owner.Dispatcher.Event('', DATA_RECEIVED, received)
|
||||||
|
self.on_receive(received)
|
||||||
|
else:
|
||||||
|
# This should never happed, so we need the debug
|
||||||
|
self.DEBUG('Unhandled data received: %s' % received,'error')
|
||||||
|
self.disconnect()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def onreceive(self, recv_handler):
|
||||||
|
if not recv_handler:
|
||||||
|
if hasattr(self._owner, 'Dispatcher'):
|
||||||
|
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
|
||||||
|
else:
|
||||||
|
self.on_receive = None
|
||||||
|
return
|
||||||
|
_tmp = self.on_receive
|
||||||
|
# make sure this cb is not overriden by recursive calls
|
||||||
|
if not recv_handler(None) and _tmp == self.on_receive:
|
||||||
|
self.on_receive = recv_handler
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
''' Closes the socket. '''
|
||||||
|
gajim.idlequeue.remove_timeout(self.fd)
|
||||||
|
gajim.idlequeue.unplug_idle(self.fd)
|
||||||
|
try:
|
||||||
|
self._sock.shutdown(socket.SHUT_RDWR)
|
||||||
|
self._sock.close()
|
||||||
|
except:
|
||||||
|
# socket is already closed
|
||||||
|
pass
|
||||||
|
self.fd = -1
|
||||||
|
self.state = -1
|
||||||
|
if self._owner:
|
||||||
|
self._owner.on_disconnect()
|
||||||
|
|
||||||
|
def _do_send(self):
|
||||||
|
if not self.sendbuff:
|
||||||
|
if not self.sendqueue:
|
||||||
|
return None # nothing to send
|
||||||
|
self.sendbuff = self.sendqueue.pop(0)
|
||||||
|
self.sent_data = self.sendbuff
|
||||||
|
try:
|
||||||
|
send_count = self._sock.send(self.sendbuff)
|
||||||
|
if send_count:
|
||||||
|
self.sendbuff = self.sendbuff[send_count:]
|
||||||
|
if not self.sendbuff and not self.sendqueue:
|
||||||
|
if self.state < 0:
|
||||||
|
gajim.idlequeue.unplug_idle(self.fd)
|
||||||
|
self._on_send()
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
# we are not waiting for write
|
||||||
|
self._plug_idle()
|
||||||
|
self._on_send()
|
||||||
|
except socket.error, e:
|
||||||
|
sys.exc_clear()
|
||||||
|
if e[0] == socket.SSL_ERROR_WANT_WRITE:
|
||||||
|
return True
|
||||||
|
if self.state < 0:
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
self._on_send_failure()
|
||||||
|
return
|
||||||
|
if self._owner.sock_type == TYPE_CLIENT:
|
||||||
|
self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _plug_idle(self):
|
||||||
|
readable = self.state != 0
|
||||||
|
if self.sendqueue or self.sendbuff:
|
||||||
|
writable = True
|
||||||
|
else:
|
||||||
|
writable = False
|
||||||
|
if self.writable != writable or self.readable != readable:
|
||||||
|
gajim.idlequeue.plug_idle(self, writable, readable)
|
||||||
|
|
||||||
|
|
||||||
|
def _on_send(self):
|
||||||
|
if self.sent_data and self.sent_data.strip():
|
||||||
|
self.DEBUG(self.sent_data,'sent')
|
||||||
|
if hasattr(self._owner, 'Dispatcher'):
|
||||||
|
self._owner.Dispatcher.Event('', DATA_SENT, self.sent_data)
|
||||||
|
self.sent_data = None
|
||||||
|
|
||||||
|
def _on_send_failure(self):
|
||||||
|
self.DEBUG("Socket error while sending data",'error')
|
||||||
|
self._owner.disconnected()
|
||||||
|
self.sent_data = None
|
||||||
|
|
||||||
|
|
||||||
|
class ClientZeroconf:
|
||||||
|
def __init__(self, caller):
|
||||||
|
self.caller = caller
|
||||||
|
self.zeroconf = None
|
||||||
|
self.roster = None
|
||||||
|
self.last_msg = ''
|
||||||
|
self.connections = {}
|
||||||
|
self.recipient_to_hash = {}
|
||||||
|
self.ip_to_hash = {}
|
||||||
|
|
||||||
|
def test_avahi(self):
|
||||||
|
try:
|
||||||
|
import avahi
|
||||||
|
except ImportError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def connect(self, show, msg):
|
||||||
|
self.port = self.start_listener(self.caller.port)
|
||||||
|
if not self.port:
|
||||||
|
return
|
||||||
|
self.zeroconf_init(show, msg)
|
||||||
|
if not self.zeroconf.connect():
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
self.roster = roster_zeroconf.Roster(self.zeroconf)
|
||||||
|
|
||||||
|
def remove_announce(self):
|
||||||
|
if self.zeroconf:
|
||||||
|
return self.zeroconf.remove_announce()
|
||||||
|
|
||||||
|
def announce(self):
|
||||||
|
if self.zeroconf:
|
||||||
|
return self.zeroconf.announce()
|
||||||
|
|
||||||
|
def set_show_msg(self, show, msg):
|
||||||
|
if self.zeroconf:
|
||||||
|
self.zeroconf.txt['msg'] = msg
|
||||||
|
self.last_msg = msg
|
||||||
|
return self.zeroconf.update_txt(show)
|
||||||
|
|
||||||
|
def resolve_all(self):
|
||||||
|
if self.zeroconf:
|
||||||
|
self.zeroconf.resolve_all()
|
||||||
|
|
||||||
|
def reannounce(self, txt):
|
||||||
|
self.remove_announce()
|
||||||
|
self.zeroconf.txt = txt
|
||||||
|
self.zeroconf.port = self.port
|
||||||
|
self.zeroconf.username = self.caller.username
|
||||||
|
return self.announce()
|
||||||
|
|
||||||
|
|
||||||
|
def zeroconf_init(self, show, msg):
|
||||||
|
self.zeroconf = zeroconf.Zeroconf(self.caller._on_new_service,
|
||||||
|
self.caller._on_remove_service, self.caller._on_name_conflictCB,
|
||||||
|
self.caller._on_disconnected, self.caller.username, self.caller.host,
|
||||||
|
self.port)
|
||||||
|
self.zeroconf.txt['msg'] = msg
|
||||||
|
self.zeroconf.txt['status'] = show
|
||||||
|
self.zeroconf.txt['1st'] = self.caller.first
|
||||||
|
self.zeroconf.txt['last'] = self.caller.last
|
||||||
|
self.zeroconf.txt['jid'] = self.caller.jabber_id
|
||||||
|
self.zeroconf.txt['email'] = self.caller.email
|
||||||
|
self.zeroconf.username = self.caller.username
|
||||||
|
self.zeroconf.host = self.caller.host
|
||||||
|
self.zeroconf.port = self.port
|
||||||
|
self.last_msg = msg
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
if self.listener:
|
||||||
|
self.listener.disconnect()
|
||||||
|
self.listener = None
|
||||||
|
if self.zeroconf:
|
||||||
|
self.zeroconf.disconnect()
|
||||||
|
self.zeroconf = None
|
||||||
|
if self.roster:
|
||||||
|
self.roster.zeroconf = None
|
||||||
|
self.roster._data = None
|
||||||
|
self.roster = None
|
||||||
|
|
||||||
|
def kill_all_connections(self):
|
||||||
|
for connection in self.connections.values():
|
||||||
|
connection.force_disconnect()
|
||||||
|
|
||||||
|
def add_connection(self, connection, ip, recipient):
|
||||||
|
sock_hash = connection.sock_hash
|
||||||
|
if sock_hash not in self.connections:
|
||||||
|
self.connections[sock_hash] = connection
|
||||||
|
self.ip_to_hash[ip] = sock_hash
|
||||||
|
if recipient:
|
||||||
|
self.recipient_to_hash[recipient] = sock_hash
|
||||||
|
|
||||||
|
def remove_connection(self, sock_hash):
|
||||||
|
if sock_hash in self.connections:
|
||||||
|
del self.connections[sock_hash]
|
||||||
|
for i in self.recipient_to_hash:
|
||||||
|
if self.recipient_to_hash[i] == sock_hash:
|
||||||
|
del self.recipient_to_hash[i]
|
||||||
|
break
|
||||||
|
for i in self.ip_to_hash:
|
||||||
|
if self.ip_to_hash[i] == sock_hash:
|
||||||
|
del self.ip_to_hash[i]
|
||||||
|
break
|
||||||
|
|
||||||
|
def start_listener(self, port):
|
||||||
|
for p in range(port, port + 5):
|
||||||
|
self.listener = ZeroconfListener(p, self)
|
||||||
|
self.listener.bind()
|
||||||
|
if self.listener.started:
|
||||||
|
return p
|
||||||
|
self.listener = None
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getRoster(self):
|
||||||
|
if self.roster:
|
||||||
|
return self.roster.getRoster()
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def send(self, msg_iq):
|
||||||
|
msg_iq.setFrom(self.roster.zeroconf.name)
|
||||||
|
to = msg_iq.getTo()
|
||||||
|
if to in self.recipient_to_hash:
|
||||||
|
conn = self.connections[self.recipient_to_hash[to]]
|
||||||
|
if conn.add_message(msg_iq):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
item = self.roster[to]
|
||||||
|
except KeyError:
|
||||||
|
#XXX invalid recipient, show some error maybe ?
|
||||||
|
return
|
||||||
|
if item['address'] in self.ip_to_hash:
|
||||||
|
conn = self.connections[self.ip_to_hash[item['address']]]
|
||||||
|
if conn.add_message(msg_iq):
|
||||||
|
return
|
||||||
|
P2PClient(None, item['address'], item['port'], self, [msg_iq], to)
|
||||||
|
|
|
@ -0,0 +1,912 @@
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Gajim Team
|
||||||
|
##
|
||||||
|
## Contributors for this file:
|
||||||
|
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||||
|
## - Nikos Kouremenos <nkour@jabber.org>
|
||||||
|
## - Dimitur Kirov <dkirov@gmail.com>
|
||||||
|
## - Travis Shirk <travis@pobox.com>
|
||||||
|
## - Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
import sha
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from calendar import timegm
|
||||||
|
|
||||||
|
#import socks5
|
||||||
|
import common.xmpp
|
||||||
|
|
||||||
|
from common import GnuPG
|
||||||
|
from common import helpers
|
||||||
|
from common import gajim
|
||||||
|
from common.zeroconf import zeroconf
|
||||||
|
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
|
'invisible']
|
||||||
|
# kind of events we can wait for an answer
|
||||||
|
VCARD_PUBLISHED = 'vcard_published'
|
||||||
|
VCARD_ARRIVED = 'vcard_arrived'
|
||||||
|
AGENT_REMOVED = 'agent_removed'
|
||||||
|
HAS_IDLE = True
|
||||||
|
try:
|
||||||
|
import common.idle as idle # when we launch gajim from sources
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
import idle # when Gajim is installed
|
||||||
|
except:
|
||||||
|
gajim.log.debug(_('Unable to load idle module'))
|
||||||
|
HAS_IDLE = False
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionBytestream:
|
||||||
|
def __init__(self):
|
||||||
|
self.files_props = {}
|
||||||
|
|
||||||
|
def is_transfer_stoped(self, file_props):
|
||||||
|
if file_props.has_key('error') and file_props['error'] != 0:
|
||||||
|
return True
|
||||||
|
if file_props.has_key('completed') and file_props['completed']:
|
||||||
|
return True
|
||||||
|
if file_props.has_key('connected') and file_props['connected'] == False:
|
||||||
|
return True
|
||||||
|
if not file_props.has_key('stopped') or not file_props['stopped']:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def send_success_connect_reply(self, streamhost):
|
||||||
|
''' send reply to the initiator of FT that we
|
||||||
|
made a connection
|
||||||
|
'''
|
||||||
|
if streamhost is None:
|
||||||
|
return None
|
||||||
|
iq = common.xmpp.Iq(to = streamhost['initiator'], typ = 'result',
|
||||||
|
frm = streamhost['target'])
|
||||||
|
iq.setAttr('id', streamhost['id'])
|
||||||
|
query = iq.setTag('query')
|
||||||
|
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
||||||
|
stream_tag = query.setTag('streamhost-used')
|
||||||
|
stream_tag.setAttr('jid', streamhost['jid'])
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def remove_transfers_for_contact(self, contact):
|
||||||
|
''' stop all active transfer for contact '''
|
||||||
|
for file_props in self.files_props.values():
|
||||||
|
if self.is_transfer_stoped(file_props):
|
||||||
|
continue
|
||||||
|
receiver_jid = unicode(file_props['receiver']).split('/')[0]
|
||||||
|
if contact.jid == receiver_jid:
|
||||||
|
file_props['error'] = -5
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
|
||||||
|
sender_jid = unicode(file_props['sender'])
|
||||||
|
if contact.jid == sender_jid:
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
|
||||||
|
def remove_all_transfers(self):
|
||||||
|
''' stops and removes all active connections from the socks5 pool '''
|
||||||
|
for file_props in self.files_props.values():
|
||||||
|
self.remove_transfer(file_props, remove_from_list = False)
|
||||||
|
del(self.files_props)
|
||||||
|
self.files_props = {}
|
||||||
|
|
||||||
|
def remove_transfer(self, file_props, remove_from_list = True):
|
||||||
|
if file_props is None:
|
||||||
|
return
|
||||||
|
self.disconnect_transfer(file_props)
|
||||||
|
sid = file_props['sid']
|
||||||
|
gajim.socks5queue.remove_file_props(self.name, sid)
|
||||||
|
|
||||||
|
if remove_from_list:
|
||||||
|
if self.files_props.has_key('sid'):
|
||||||
|
del(self.files_props['sid'])
|
||||||
|
|
||||||
|
def disconnect_transfer(self, file_props):
|
||||||
|
if file_props is None:
|
||||||
|
return
|
||||||
|
if file_props.has_key('hash'):
|
||||||
|
gajim.socks5queue.remove_sender(file_props['hash'])
|
||||||
|
|
||||||
|
if file_props.has_key('streamhosts'):
|
||||||
|
for host in file_props['streamhosts']:
|
||||||
|
if host.has_key('idx') and host['idx'] > 0:
|
||||||
|
gajim.socks5queue.remove_receiver(host['idx'])
|
||||||
|
gajim.socks5queue.remove_sender(host['idx'])
|
||||||
|
|
||||||
|
def send_socks5_info(self, file_props, fast = True, receiver = None,
|
||||||
|
sender = None):
|
||||||
|
''' send iq for the present streamhosts and proxies '''
|
||||||
|
if type(self.peerhost) != tuple:
|
||||||
|
return
|
||||||
|
port = gajim.config.get('file_transfers_port')
|
||||||
|
ft_override_host_to_send = gajim.config.get('ft_override_host_to_send')
|
||||||
|
if receiver is None:
|
||||||
|
receiver = file_props['receiver']
|
||||||
|
if sender is None:
|
||||||
|
sender = file_props['sender']
|
||||||
|
proxyhosts = []
|
||||||
|
sha_str = helpers.get_auth_sha(file_props['sid'], sender,
|
||||||
|
receiver)
|
||||||
|
file_props['sha_str'] = sha_str
|
||||||
|
if not ft_override_host_to_send:
|
||||||
|
ft_override_host_to_send = self.peerhost[0]
|
||||||
|
try:
|
||||||
|
ft_override_host_to_send = socket.gethostbyname(
|
||||||
|
ft_override_host_to_send)
|
||||||
|
except socket.gaierror:
|
||||||
|
self.dispatch('ERROR', (_('Wrong host'), _('The host you configured as the ft_override_host_to_send advanced option is not valid, so ignored.')))
|
||||||
|
ft_override_host_to_send = self.peerhost[0]
|
||||||
|
listener = gajim.socks5queue.start_listener(port,
|
||||||
|
sha_str, self._result_socks5_sid, file_props['sid'])
|
||||||
|
if listener == None:
|
||||||
|
file_props['error'] = -5
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (unicode(receiver), file_props,
|
||||||
|
''))
|
||||||
|
self._connect_error(unicode(receiver), file_props['sid'],
|
||||||
|
file_props['sid'], code = 406)
|
||||||
|
return
|
||||||
|
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq', to = unicode(receiver),
|
||||||
|
typ = 'set')
|
||||||
|
file_props['request-id'] = 'id_' + file_props['sid']
|
||||||
|
iq.setID(file_props['request-id'])
|
||||||
|
query = iq.setTag('query')
|
||||||
|
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
||||||
|
query.setAttr('mode', 'tcp')
|
||||||
|
query.setAttr('sid', file_props['sid'])
|
||||||
|
streamhost = query.setTag('streamhost')
|
||||||
|
streamhost.setAttr('port', unicode(port))
|
||||||
|
streamhost.setAttr('host', ft_override_host_to_send)
|
||||||
|
streamhost.setAttr('jid', sender)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def send_file_rejection(self, file_props):
|
||||||
|
''' informs sender that we refuse to download the file '''
|
||||||
|
# user response to ConfirmationDialog may come after we've disconneted
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq',
|
||||||
|
to = unicode(file_props['sender']), typ = 'error')
|
||||||
|
iq.setAttr('id', file_props['request-id'])
|
||||||
|
err = common.xmpp.ErrorNode(code = '403', typ = 'cancel', name =
|
||||||
|
'forbidden', text = 'Offer Declined')
|
||||||
|
iq.addChild(node=err)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def send_file_approval(self, file_props):
|
||||||
|
''' send iq, confirming that we want to download the file '''
|
||||||
|
# user response to ConfirmationDialog may come after we've disconneted
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq',
|
||||||
|
to = unicode(file_props['sender']), typ = 'result')
|
||||||
|
iq.setAttr('id', file_props['request-id'])
|
||||||
|
si = iq.setTag('si')
|
||||||
|
si.setNamespace(common.xmpp.NS_SI)
|
||||||
|
if file_props.has_key('offset') and file_props['offset']:
|
||||||
|
file_tag = si.setTag('file')
|
||||||
|
file_tag.setNamespace(common.xmpp.NS_FILE)
|
||||||
|
range_tag = file_tag.setTag('range')
|
||||||
|
range_tag.setAttr('offset', file_props['offset'])
|
||||||
|
feature = si.setTag('feature')
|
||||||
|
feature.setNamespace(common.xmpp.NS_FEATURE)
|
||||||
|
_feature = common.xmpp.DataForm(typ='submit')
|
||||||
|
feature.addChild(node=_feature)
|
||||||
|
field = _feature.setField('stream-method')
|
||||||
|
field.delAttr('type')
|
||||||
|
field.setValue(common.xmpp.NS_BYTESTREAM)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def send_file_request(self, file_props):
|
||||||
|
''' send iq for new FT request '''
|
||||||
|
if not self.connection or self.connected < 2:
|
||||||
|
return
|
||||||
|
our_jid = gajim.get_jid_from_account(self.name)
|
||||||
|
frm = our_jid
|
||||||
|
file_props['sender'] = frm
|
||||||
|
fjid = file_props['receiver'].jid
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq', to = fjid,
|
||||||
|
typ = 'set')
|
||||||
|
iq.setID(file_props['sid'])
|
||||||
|
self.files_props[file_props['sid']] = file_props
|
||||||
|
si = iq.setTag('si')
|
||||||
|
si.setNamespace(common.xmpp.NS_SI)
|
||||||
|
si.setAttr('profile', common.xmpp.NS_FILE)
|
||||||
|
si.setAttr('id', file_props['sid'])
|
||||||
|
file_tag = si.setTag('file')
|
||||||
|
file_tag.setNamespace(common.xmpp.NS_FILE)
|
||||||
|
file_tag.setAttr('name', file_props['name'])
|
||||||
|
file_tag.setAttr('size', file_props['size'])
|
||||||
|
desc = file_tag.setTag('desc')
|
||||||
|
if file_props.has_key('desc'):
|
||||||
|
desc.setData(file_props['desc'])
|
||||||
|
file_tag.setTag('range')
|
||||||
|
feature = si.setTag('feature')
|
||||||
|
feature.setNamespace(common.xmpp.NS_FEATURE)
|
||||||
|
_feature = common.xmpp.DataForm(typ='form')
|
||||||
|
feature.addChild(node=_feature)
|
||||||
|
field = _feature.setField('stream-method')
|
||||||
|
field.setAttr('type', 'list-single')
|
||||||
|
field.addOption(common.xmpp.NS_BYTESTREAM)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def _result_socks5_sid(self, sid, hash_id):
|
||||||
|
''' store the result of sha message from auth. '''
|
||||||
|
if not self.files_props.has_key(sid):
|
||||||
|
return
|
||||||
|
file_props = self.files_props[sid]
|
||||||
|
file_props['hash'] = hash_id
|
||||||
|
return
|
||||||
|
|
||||||
|
def _connect_error(self, to, _id, sid, code = 404):
|
||||||
|
''' cb, when there is an error establishing BS connection, or
|
||||||
|
when connection is rejected'''
|
||||||
|
msg_dict = {
|
||||||
|
404: 'Could not connect to given hosts',
|
||||||
|
405: 'Cancel',
|
||||||
|
406: 'Not acceptable',
|
||||||
|
}
|
||||||
|
msg = msg_dict[code]
|
||||||
|
iq = None
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq', to = to,
|
||||||
|
typ = 'error')
|
||||||
|
iq.setAttr('id', _id)
|
||||||
|
err = iq.setTag('error')
|
||||||
|
err.setAttr('code', unicode(code))
|
||||||
|
err.setData(msg)
|
||||||
|
self.connection.send(iq)
|
||||||
|
if code == 404:
|
||||||
|
file_props = gajim.socks5queue.get_file_props(self.name, sid)
|
||||||
|
if file_props is not None:
|
||||||
|
self.disconnect_transfer(file_props)
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
|
||||||
|
|
||||||
|
def _proxy_auth_ok(self, proxy):
|
||||||
|
'''cb, called after authentication to proxy server '''
|
||||||
|
file_props = self.files_props[proxy['sid']]
|
||||||
|
iq = common.xmpp.Protocol(name = 'iq', to = proxy['initiator'],
|
||||||
|
typ = 'set')
|
||||||
|
auth_id = "au_" + proxy['sid']
|
||||||
|
iq.setID(auth_id)
|
||||||
|
query = iq.setTag('query')
|
||||||
|
query.setNamespace(common.xmpp.NS_BYTESTREAM)
|
||||||
|
query.setAttr('sid', proxy['sid'])
|
||||||
|
activate = query.setTag('activate')
|
||||||
|
activate.setData(file_props['proxy_receiver'])
|
||||||
|
iq.setID(auth_id)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
# register xmpppy handlers for bytestream and FT stanzas
|
||||||
|
def _bytestreamErrorCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_bytestreamErrorCB')
|
||||||
|
id = unicode(iq_obj.getAttr('id'))
|
||||||
|
frm = unicode(iq_obj.getFrom())
|
||||||
|
query = iq_obj.getTag('query')
|
||||||
|
gajim.proxy65_manager.error_cb(frm, query)
|
||||||
|
jid = unicode(iq_obj.getFrom())
|
||||||
|
id = id[3:]
|
||||||
|
if not self.files_props.has_key(id):
|
||||||
|
return
|
||||||
|
file_props = self.files_props[id]
|
||||||
|
file_props['error'] = -4
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _bytestreamSetCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_bytestreamSetCB')
|
||||||
|
target = unicode(iq_obj.getAttr('to'))
|
||||||
|
id = unicode(iq_obj.getAttr('id'))
|
||||||
|
query = iq_obj.getTag('query')
|
||||||
|
sid = unicode(query.getAttr('sid'))
|
||||||
|
file_props = gajim.socks5queue.get_file_props(
|
||||||
|
self.name, sid)
|
||||||
|
streamhosts=[]
|
||||||
|
for item in query.getChildren():
|
||||||
|
if item.getName() == 'streamhost':
|
||||||
|
host_dict={
|
||||||
|
'state': 0,
|
||||||
|
'target': target,
|
||||||
|
'id': id,
|
||||||
|
'sid': sid,
|
||||||
|
'initiator': unicode(iq_obj.getFrom())
|
||||||
|
}
|
||||||
|
for attr in item.getAttrs():
|
||||||
|
host_dict[attr] = item.getAttr(attr)
|
||||||
|
streamhosts.append(host_dict)
|
||||||
|
if file_props is None:
|
||||||
|
if self.files_props.has_key(sid):
|
||||||
|
file_props = self.files_props[sid]
|
||||||
|
file_props['fast'] = streamhosts
|
||||||
|
if file_props['type'] == 's':
|
||||||
|
if file_props.has_key('streamhosts'):
|
||||||
|
file_props['streamhosts'].extend(streamhosts)
|
||||||
|
else:
|
||||||
|
file_props['streamhosts'] = streamhosts
|
||||||
|
if not gajim.socks5queue.get_file_props(self.name, sid):
|
||||||
|
gajim.socks5queue.add_file_props(self.name, file_props)
|
||||||
|
gajim.socks5queue.connect_to_hosts(self.name, sid,
|
||||||
|
self.send_success_connect_reply, None)
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
file_props['streamhosts'] = streamhosts
|
||||||
|
if file_props['type'] == 'r':
|
||||||
|
gajim.socks5queue.connect_to_hosts(self.name, sid,
|
||||||
|
self.send_success_connect_reply, self._connect_error)
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _ResultCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_ResultCB')
|
||||||
|
# if we want to respect jep-0065 we have to check for proxy
|
||||||
|
# activation result in any result iq
|
||||||
|
real_id = unicode(iq_obj.getAttr('id'))
|
||||||
|
if real_id[:3] != 'au_':
|
||||||
|
return
|
||||||
|
frm = unicode(iq_obj.getFrom())
|
||||||
|
id = real_id[3:]
|
||||||
|
if self.files_props.has_key(id):
|
||||||
|
file_props = self.files_props[id]
|
||||||
|
if file_props['streamhost-used']:
|
||||||
|
for host in file_props['proxyhosts']:
|
||||||
|
if host['initiator'] == frm and host.has_key('idx'):
|
||||||
|
gajim.socks5queue.activate_proxy(host['idx'])
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _bytestreamResultCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_bytestreamResultCB')
|
||||||
|
frm = unicode(iq_obj.getFrom())
|
||||||
|
real_id = unicode(iq_obj.getAttr('id'))
|
||||||
|
query = iq_obj.getTag('query')
|
||||||
|
gajim.proxy65_manager.resolve_result(frm, query)
|
||||||
|
|
||||||
|
try:
|
||||||
|
streamhost = query.getTag('streamhost-used')
|
||||||
|
except: # this bytestream result is not what we need
|
||||||
|
pass
|
||||||
|
id = real_id[3:]
|
||||||
|
if self.files_props.has_key(id):
|
||||||
|
file_props = self.files_props[id]
|
||||||
|
else:
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
if streamhost is None:
|
||||||
|
# proxy approves the activate query
|
||||||
|
if real_id[:3] == 'au_':
|
||||||
|
id = real_id[3:]
|
||||||
|
if not file_props.has_key('streamhost-used') or \
|
||||||
|
file_props['streamhost-used'] is False:
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
if not file_props.has_key('proxyhosts'):
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
for host in file_props['proxyhosts']:
|
||||||
|
if host['initiator'] == frm and \
|
||||||
|
unicode(query.getAttr('sid')) == file_props['sid']:
|
||||||
|
gajim.socks5queue.activate_proxy(host['idx'])
|
||||||
|
break
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
jid = streamhost.getAttr('jid')
|
||||||
|
if file_props.has_key('streamhost-used') and \
|
||||||
|
file_props['streamhost-used'] is True:
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
if real_id[:3] == 'au_':
|
||||||
|
gajim.socks5queue.send_file(file_props, self.name)
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
proxy = None
|
||||||
|
if file_props.has_key('proxyhosts'):
|
||||||
|
for proxyhost in file_props['proxyhosts']:
|
||||||
|
if proxyhost['jid'] == jid:
|
||||||
|
proxy = proxyhost
|
||||||
|
|
||||||
|
if proxy != None:
|
||||||
|
file_props['streamhost-used'] = True
|
||||||
|
if not file_props.has_key('streamhosts'):
|
||||||
|
file_props['streamhosts'] = []
|
||||||
|
file_props['streamhosts'].append(proxy)
|
||||||
|
file_props['is_a_proxy'] = True
|
||||||
|
receiver = socks5.Socks5Receiver(gajim.idlequeue, proxy, file_props['sid'], file_props)
|
||||||
|
gajim.socks5queue.add_receiver(self.name, receiver)
|
||||||
|
proxy['idx'] = receiver.queue_idx
|
||||||
|
gajim.socks5queue.on_success = self._proxy_auth_ok
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
else:
|
||||||
|
gajim.socks5queue.send_file(file_props, self.name)
|
||||||
|
if file_props.has_key('fast'):
|
||||||
|
fasts = file_props['fast']
|
||||||
|
if len(fasts) > 0:
|
||||||
|
self._connect_error(frm, fasts[0]['id'], file_props['sid'],
|
||||||
|
code = 406)
|
||||||
|
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _siResultCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_siResultCB')
|
||||||
|
self.peerhost = con._owner.Connection._sock.getsockname()
|
||||||
|
id = iq_obj.getAttr('id')
|
||||||
|
if not self.files_props.has_key(id):
|
||||||
|
# no such jid
|
||||||
|
return
|
||||||
|
file_props = self.files_props[id]
|
||||||
|
if file_props is None:
|
||||||
|
# file properties for jid is none
|
||||||
|
return
|
||||||
|
if file_props.has_key('request-id'):
|
||||||
|
# we have already sent streamhosts info
|
||||||
|
return
|
||||||
|
file_props['receiver'] = unicode(iq_obj.getFrom())
|
||||||
|
si = iq_obj.getTag('si')
|
||||||
|
file_tag = si.getTag('file')
|
||||||
|
range_tag = None
|
||||||
|
if file_tag:
|
||||||
|
range_tag = file_tag.getTag('range')
|
||||||
|
if range_tag:
|
||||||
|
offset = range_tag.getAttr('offset')
|
||||||
|
if offset:
|
||||||
|
file_props['offset'] = int(offset)
|
||||||
|
length = range_tag.getAttr('length')
|
||||||
|
if length:
|
||||||
|
file_props['length'] = int(length)
|
||||||
|
feature = si.setTag('feature')
|
||||||
|
if feature.getNamespace() != common.xmpp.NS_FEATURE:
|
||||||
|
return
|
||||||
|
form_tag = feature.getTag('x')
|
||||||
|
form = common.xmpp.DataForm(node=form_tag)
|
||||||
|
field = form.getField('stream-method')
|
||||||
|
if field.getValue() != common.xmpp.NS_BYTESTREAM:
|
||||||
|
return
|
||||||
|
self.send_socks5_info(file_props, fast = True)
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _siSetCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_siSetCB')
|
||||||
|
jid = unicode(iq_obj.getFrom())
|
||||||
|
si = iq_obj.getTag('si')
|
||||||
|
profile = si.getAttr('profile')
|
||||||
|
mime_type = si.getAttr('mime-type')
|
||||||
|
if profile != common.xmpp.NS_FILE:
|
||||||
|
return
|
||||||
|
file_tag = si.getTag('file')
|
||||||
|
file_props = {'type': 'r'}
|
||||||
|
for attribute in file_tag.getAttrs():
|
||||||
|
if attribute in ('name', 'size', 'hash', 'date'):
|
||||||
|
val = file_tag.getAttr(attribute)
|
||||||
|
if val is None:
|
||||||
|
continue
|
||||||
|
file_props[attribute] = val
|
||||||
|
file_desc_tag = file_tag.getTag('desc')
|
||||||
|
if file_desc_tag is not None:
|
||||||
|
file_props['desc'] = file_desc_tag.getData()
|
||||||
|
|
||||||
|
if mime_type is not None:
|
||||||
|
file_props['mime-type'] = mime_type
|
||||||
|
our_jid = gajim.get_jid_from_account(self.name)
|
||||||
|
file_props['receiver'] = our_jid
|
||||||
|
file_props['sender'] = unicode(iq_obj.getFrom())
|
||||||
|
file_props['request-id'] = unicode(iq_obj.getAttr('id'))
|
||||||
|
file_props['sid'] = unicode(si.getAttr('id'))
|
||||||
|
gajim.socks5queue.add_file_props(self.name, file_props)
|
||||||
|
self.dispatch('FILE_REQUEST', (jid, file_props))
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _siErrorCB(self, con, iq_obj):
|
||||||
|
gajim.log.debug('_siErrorCB')
|
||||||
|
si = iq_obj.getTag('si')
|
||||||
|
profile = si.getAttr('profile')
|
||||||
|
if profile != common.xmpp.NS_FILE:
|
||||||
|
return
|
||||||
|
id = iq_obj.getAttr('id')
|
||||||
|
if not self.files_props.has_key(id):
|
||||||
|
# no such jid
|
||||||
|
return
|
||||||
|
file_props = self.files_props[id]
|
||||||
|
if file_props is None:
|
||||||
|
# file properties for jid is none
|
||||||
|
return
|
||||||
|
jid = unicode(iq_obj.getFrom())
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
|
||||||
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionVcard:
|
||||||
|
def __init__(self):
|
||||||
|
self.vcard_sha = None
|
||||||
|
self.vcard_shas = {} # sha of contacts
|
||||||
|
self.room_jids = [] # list of gc jids so that vcard are saved in a folder
|
||||||
|
|
||||||
|
def add_sha(self, p, send_caps = True):
|
||||||
|
'''
|
||||||
|
c = p.setTag('x', namespace = common.xmpp.NS_VCARD_UPDATE)
|
||||||
|
if self.vcard_sha is not None:
|
||||||
|
c.setTagData('photo', self.vcard_sha)
|
||||||
|
if send_caps:
|
||||||
|
return self.add_caps(p)
|
||||||
|
return p
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_caps(self, p):
|
||||||
|
'''
|
||||||
|
# advertise our capabilities in presence stanza (jep-0115)
|
||||||
|
c = p.setTag('c', namespace = common.xmpp.NS_CAPS)
|
||||||
|
c.setAttr('node', 'http://gajim.org/caps')
|
||||||
|
c.setAttr('ext', 'ftrans')
|
||||||
|
c.setAttr('ver', gajim.version)
|
||||||
|
return p
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def node_to_dict(self, node):
|
||||||
|
dict = {}
|
||||||
|
|
||||||
|
for info in node.getChildren():
|
||||||
|
name = info.getName()
|
||||||
|
if name in ('ADR', 'TEL', 'EMAIL'): # we can have several
|
||||||
|
if not dict.has_key(name):
|
||||||
|
dict[name] = []
|
||||||
|
entry = {}
|
||||||
|
for c in info.getChildren():
|
||||||
|
entry[c.getName()] = c.getData()
|
||||||
|
dict[name].append(entry)
|
||||||
|
elif info.getChildren() == []:
|
||||||
|
dict[name] = info.getData()
|
||||||
|
else:
|
||||||
|
dict[name] = {}
|
||||||
|
for c in info.getChildren():
|
||||||
|
dict[name][c.getName()] = c.getData()
|
||||||
|
|
||||||
|
return dict
|
||||||
|
|
||||||
|
def save_vcard_to_hd(self, full_jid, card):
|
||||||
|
jid, nick = gajim.get_room_and_nick_from_fjid(full_jid)
|
||||||
|
puny_jid = helpers.sanitize_filename(jid)
|
||||||
|
path = os.path.join(gajim.VCARD_PATH, puny_jid)
|
||||||
|
if jid in self.room_jids or os.path.isdir(path):
|
||||||
|
# remove room_jid file if needed
|
||||||
|
if os.path.isfile(path):
|
||||||
|
os.remove(path)
|
||||||
|
# create folder if needed
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
os.mkdir(path, 0700)
|
||||||
|
puny_nick = helpers.sanitize_filename(nick)
|
||||||
|
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
|
||||||
|
else:
|
||||||
|
path_to_file = path
|
||||||
|
fil = open(path_to_file, 'w')
|
||||||
|
fil.write(str(card))
|
||||||
|
fil.close()
|
||||||
|
|
||||||
|
def get_cached_vcard(self, fjid, is_fake_jid = False):
|
||||||
|
'''return the vcard as a dict
|
||||||
|
return {} if vcard was too old
|
||||||
|
return None if we don't have cached vcard'''
|
||||||
|
jid, nick = gajim.get_room_and_nick_from_fjid(fjid)
|
||||||
|
puny_jid = helpers.sanitize_filename(jid)
|
||||||
|
if is_fake_jid:
|
||||||
|
puny_nick = helpers.sanitize_filename(nick)
|
||||||
|
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
|
||||||
|
else:
|
||||||
|
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid)
|
||||||
|
if not os.path.isfile(path_to_file):
|
||||||
|
return None
|
||||||
|
# We have the vcard cached
|
||||||
|
f = open(path_to_file)
|
||||||
|
c = f.read()
|
||||||
|
f.close()
|
||||||
|
card = common.xmpp.Node(node = c)
|
||||||
|
vcard = self.node_to_dict(card)
|
||||||
|
if vcard.has_key('PHOTO'):
|
||||||
|
if not isinstance(vcard['PHOTO'], dict):
|
||||||
|
del vcard['PHOTO']
|
||||||
|
elif vcard['PHOTO'].has_key('SHA'):
|
||||||
|
cached_sha = vcard['PHOTO']['SHA']
|
||||||
|
if self.vcard_shas.has_key(jid) and self.vcard_shas[jid] != \
|
||||||
|
cached_sha:
|
||||||
|
# user change his vcard so don't use the cached one
|
||||||
|
return {}
|
||||||
|
vcard['jid'] = jid
|
||||||
|
vcard['resource'] = gajim.get_resource_from_jid(fjid)
|
||||||
|
return vcard
|
||||||
|
|
||||||
|
def request_vcard(self, jid = None, is_fake_jid = False):
|
||||||
|
'''request the VCARD. If is_fake_jid is True, it means we request a vcard
|
||||||
|
to a fake jid, like in private messages in groupchat'''
|
||||||
|
if not self.connection:
|
||||||
|
return
|
||||||
|
'''
|
||||||
|
iq = common.xmpp.Iq(typ = 'get')
|
||||||
|
if jid:
|
||||||
|
iq.setTo(jid)
|
||||||
|
iq.setTag(common.xmpp.NS_VCARD + ' vCard')
|
||||||
|
|
||||||
|
id = self.connection.getAnID()
|
||||||
|
iq.setID(id)
|
||||||
|
self.awaiting_answers[id] = (VCARD_ARRIVED, jid)
|
||||||
|
if is_fake_jid:
|
||||||
|
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||||
|
if not room_jid in self.room_jids:
|
||||||
|
self.room_jids.append(room_jid)
|
||||||
|
self.connection.send(iq)
|
||||||
|
#('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...})
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send_vcard(self, vcard):
|
||||||
|
if not self.connection:
|
||||||
|
return
|
||||||
|
'''
|
||||||
|
iq = common.xmpp.Iq(typ = 'set')
|
||||||
|
iq2 = iq.setTag(common.xmpp.NS_VCARD + ' vCard')
|
||||||
|
for i in vcard:
|
||||||
|
if i == 'jid':
|
||||||
|
continue
|
||||||
|
if isinstance(vcard[i], dict):
|
||||||
|
iq3 = iq2.addChild(i)
|
||||||
|
for j in vcard[i]:
|
||||||
|
iq3.addChild(j).setData(vcard[i][j])
|
||||||
|
elif type(vcard[i]) == type([]):
|
||||||
|
for j in vcard[i]:
|
||||||
|
iq3 = iq2.addChild(i)
|
||||||
|
for k in j:
|
||||||
|
iq3.addChild(k).setData(j[k])
|
||||||
|
else:
|
||||||
|
iq2.addChild(i).setData(vcard[i])
|
||||||
|
|
||||||
|
id = self.connection.getAnID()
|
||||||
|
iq.setID(id)
|
||||||
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
# Add the sha of the avatar
|
||||||
|
if vcard.has_key('PHOTO') and isinstance(vcard['PHOTO'], dict) and \
|
||||||
|
vcard['PHOTO'].has_key('BINVAL'):
|
||||||
|
photo = vcard['PHOTO']['BINVAL']
|
||||||
|
photo_decoded = base64.decodestring(photo)
|
||||||
|
our_jid = gajim.get_jid_from_account(self.name)
|
||||||
|
gajim.interface.save_avatar_files(our_jid, photo_decoded)
|
||||||
|
avatar_sha = sha.sha(photo_decoded).hexdigest()
|
||||||
|
iq2.getTag('PHOTO').setTagData('SHA', avatar_sha)
|
||||||
|
|
||||||
|
self.awaiting_answers[id] = (VCARD_PUBLISHED, iq2)
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
|
||||||
|
def __init__(self):
|
||||||
|
ConnectionVcard.__init__(self)
|
||||||
|
ConnectionBytestream.__init__(self)
|
||||||
|
# List of IDs we are waiting answers for {id: (type_of_request, data), }
|
||||||
|
self.awaiting_answers = {}
|
||||||
|
# List of IDs that will produce a timeout is answer doesn't arrive
|
||||||
|
# {time_of_the_timeout: (id, message to send to gui), }
|
||||||
|
self.awaiting_timeouts = {}
|
||||||
|
# keep the jids we auto added (transports contacts) to not send the
|
||||||
|
# SUBSCRIBED event to gui
|
||||||
|
self.automatically_added = []
|
||||||
|
try:
|
||||||
|
idle.init()
|
||||||
|
except:
|
||||||
|
HAS_IDLE = False
|
||||||
|
|
||||||
|
def _messageCB(self, ip, con, msg):
|
||||||
|
'''Called when we receive a message'''
|
||||||
|
msgtxt = msg.getBody()
|
||||||
|
msghtml = msg.getXHTML()
|
||||||
|
mtype = msg.getType()
|
||||||
|
subject = msg.getSubject() # if not there, it's None
|
||||||
|
tim = msg.getTimestamp()
|
||||||
|
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
|
||||||
|
tim = time.localtime(timegm(tim))
|
||||||
|
frm = msg.getFrom()
|
||||||
|
if frm == None:
|
||||||
|
for key in self.connection.zeroconf.contacts:
|
||||||
|
if ip == self.connection.zeroconf.contacts[key][zeroconf.C_ADDRESS]:
|
||||||
|
frm = key
|
||||||
|
frm = str(frm)
|
||||||
|
jid = frm
|
||||||
|
no_log_for = gajim.config.get_per('accounts', self.name,
|
||||||
|
'no_log_for').split()
|
||||||
|
encrypted = False
|
||||||
|
chatstate = None
|
||||||
|
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
|
||||||
|
decmsg = ''
|
||||||
|
# invitations
|
||||||
|
invite = None
|
||||||
|
if not encTag:
|
||||||
|
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
|
||||||
|
if invite and not invite.getTag('invite'):
|
||||||
|
invite = None
|
||||||
|
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None
|
||||||
|
msg_id = None
|
||||||
|
composing_jep = None
|
||||||
|
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED
|
||||||
|
# invitation
|
||||||
|
# stanza (MUC JEP) remove in 2007, as we do not do NOT RECOMMENDED
|
||||||
|
xtags = msg.getTags('x')
|
||||||
|
# chatstates - look for chatstate tags in a message if not delayed
|
||||||
|
if not delayed:
|
||||||
|
composing_jep = False
|
||||||
|
children = msg.getChildren()
|
||||||
|
for child in children:
|
||||||
|
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
|
||||||
|
chatstate = child.getName()
|
||||||
|
composing_jep = 'JEP-0085'
|
||||||
|
break
|
||||||
|
# No JEP-0085 support, fallback to JEP-0022
|
||||||
|
if not chatstate:
|
||||||
|
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
|
||||||
|
if chatstate_child:
|
||||||
|
chatstate = 'active'
|
||||||
|
composing_jep = 'JEP-0022'
|
||||||
|
if not msgtxt and chatstate_child.getTag('composing'):
|
||||||
|
chatstate = 'composing'
|
||||||
|
# JEP-0172 User Nickname
|
||||||
|
user_nick = msg.getTagData('nick')
|
||||||
|
if not user_nick:
|
||||||
|
user_nick = ''
|
||||||
|
|
||||||
|
if encTag and GnuPG.USE_GPG:
|
||||||
|
#decrypt
|
||||||
|
encmsg = encTag.getData()
|
||||||
|
|
||||||
|
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
||||||
|
if keyID:
|
||||||
|
decmsg = self.gpg.decrypt(encmsg, keyID)
|
||||||
|
if decmsg:
|
||||||
|
msgtxt = decmsg
|
||||||
|
encrypted = True
|
||||||
|
if mtype == 'error':
|
||||||
|
error_msg = msg.getError()
|
||||||
|
if not error_msg:
|
||||||
|
error_msg = msgtxt
|
||||||
|
msgtxt = None
|
||||||
|
if self.name not in no_log_for:
|
||||||
|
gajim.logger.write('error', frm, error_msg, tim = tim,
|
||||||
|
subject = subject)
|
||||||
|
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
|
||||||
|
tim))
|
||||||
|
elif mtype == 'chat': # it's type 'chat'
|
||||||
|
if not msg.getTag('body') and chatstate is None: #no <body>
|
||||||
|
return
|
||||||
|
if msg.getTag('body') and self.name not in no_log_for and jid not in\
|
||||||
|
no_log_for and msgtxt:
|
||||||
|
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim,
|
||||||
|
subject = subject)
|
||||||
|
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject,
|
||||||
|
chatstate, msg_id, composing_jep, user_nick, msghtml))
|
||||||
|
elif mtype == 'normal': # it's single message
|
||||||
|
if self.name not in no_log_for and jid not in no_log_for and msgtxt:
|
||||||
|
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
|
||||||
|
subject = subject)
|
||||||
|
if invite:
|
||||||
|
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, 'normal',
|
||||||
|
subject, chatstate, msg_id, composing_jep, user_nick))
|
||||||
|
# END messageCB
|
||||||
|
'''
|
||||||
|
def build_http_auth_answer(self, iq_obj, answer):
|
||||||
|
if answer == 'yes':
|
||||||
|
iq = iq_obj.buildReply('result')
|
||||||
|
elif answer == 'no':
|
||||||
|
iq = iq_obj.buildReply('error')
|
||||||
|
iq.setError('not-authorized', 401)
|
||||||
|
self.connection.send(iq)
|
||||||
|
'''
|
||||||
|
|
||||||
|
def parse_data_form(self, node):
|
||||||
|
dic = {}
|
||||||
|
tag = node.getTag('title')
|
||||||
|
if tag:
|
||||||
|
dic['title'] = tag.getData()
|
||||||
|
tag = node.getTag('instructions')
|
||||||
|
if tag:
|
||||||
|
dic['instructions'] = tag.getData()
|
||||||
|
i = 0
|
||||||
|
for child in node.getChildren():
|
||||||
|
if child.getName() != 'field':
|
||||||
|
continue
|
||||||
|
var = child.getAttr('var')
|
||||||
|
ctype = child.getAttr('type')
|
||||||
|
label = child.getAttr('label')
|
||||||
|
if not var and ctype != 'fixed': # We must have var if type != fixed
|
||||||
|
continue
|
||||||
|
dic[i] = {}
|
||||||
|
if var:
|
||||||
|
dic[i]['var'] = var
|
||||||
|
if ctype:
|
||||||
|
dic[i]['type'] = ctype
|
||||||
|
if label:
|
||||||
|
dic[i]['label'] = label
|
||||||
|
tags = child.getTags('value')
|
||||||
|
if len(tags):
|
||||||
|
dic[i]['values'] = []
|
||||||
|
for tag in tags:
|
||||||
|
data = tag.getData()
|
||||||
|
if ctype == 'boolean':
|
||||||
|
if data in ('yes', 'true', 'assent', '1'):
|
||||||
|
data = True
|
||||||
|
else:
|
||||||
|
data = False
|
||||||
|
dic[i]['values'].append(data)
|
||||||
|
tag = child.getTag('desc')
|
||||||
|
if tag:
|
||||||
|
dic[i]['desc'] = tag.getData()
|
||||||
|
option_tags = child.getTags('option')
|
||||||
|
if len(option_tags):
|
||||||
|
dic[i]['options'] = {}
|
||||||
|
j = 0
|
||||||
|
for option_tag in option_tags:
|
||||||
|
dic[i]['options'][j] = {}
|
||||||
|
label = option_tag.getAttr('label')
|
||||||
|
tags = option_tag.getTags('value')
|
||||||
|
dic[i]['options'][j]['values'] = []
|
||||||
|
for tag in tags:
|
||||||
|
dic[i]['options'][j]['values'].append(tag.getData())
|
||||||
|
if not label:
|
||||||
|
label = dic[i]['options'][j]['values'][0]
|
||||||
|
dic[i]['options'][j]['label'] = label
|
||||||
|
j += 1
|
||||||
|
if not dic[i].has_key('values'):
|
||||||
|
dic[i]['values'] = [dic[i]['options'][0]['values'][0]]
|
||||||
|
i += 1
|
||||||
|
return dic
|
||||||
|
|
||||||
|
def store_metacontacts(self, tags):
|
||||||
|
''' fake empty method '''
|
||||||
|
# serverside metacontacts are not supported with zeroconf
|
||||||
|
# (there is no server)
|
||||||
|
pass
|
||||||
|
def remove_transfers_for_contact(self, contact):
|
||||||
|
''' stop all active transfer for contact '''
|
||||||
|
'''for file_props in self.files_props.values():
|
||||||
|
if self.is_transfer_stoped(file_props):
|
||||||
|
continue
|
||||||
|
receiver_jid = unicode(file_props['receiver']).split('/')[0]
|
||||||
|
if contact.jid == receiver_jid:
|
||||||
|
file_props['error'] = -5
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props))
|
||||||
|
sender_jid = unicode(file_props['sender']).split('/')[0]
|
||||||
|
if contact.jid == sender_jid:
|
||||||
|
file_props['error'] = -3
|
||||||
|
self.remove_transfer(file_props)
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_all_transfers(self):
|
||||||
|
''' stops and removes all active connections from the socks5 pool '''
|
||||||
|
'''
|
||||||
|
for file_props in self.files_props.values():
|
||||||
|
self.remove_transfer(file_props, remove_from_list = False)
|
||||||
|
del(self.files_props)
|
||||||
|
self.files_props = {}
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_transfer(self, file_props, remove_from_list = True):
|
||||||
|
'''
|
||||||
|
if file_props is None:
|
||||||
|
return
|
||||||
|
self.disconnect_transfer(file_props)
|
||||||
|
sid = file_props['sid']
|
||||||
|
gajim.socks5queue.remove_file_props(self.name, sid)
|
||||||
|
|
||||||
|
if remove_from_list:
|
||||||
|
if self.files_props.has_key('sid'):
|
||||||
|
del(self.files_props['sid'])
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,487 @@
|
||||||
|
## common/zeroconf/connection_zeroconf.py
|
||||||
|
##
|
||||||
|
## Contributors for this file:
|
||||||
|
## - Yann Le Boulanger <asterix@lagaule.org>
|
||||||
|
## - Nikos Kouremenos <nkour@jabber.org>
|
||||||
|
## - Dimitur Kirov <dkirov@gmail.com>
|
||||||
|
## - Travis Shirk <travis@pobox.com>
|
||||||
|
## - Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
|
||||||
|
## Vincent Hanquez <tab@snarc.org>
|
||||||
|
## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org>
|
||||||
|
## Vincent Hanquez <tab@snarc.org>
|
||||||
|
## Nikos Kouremenos <nkour@jabber.org>
|
||||||
|
## Dimitur Kirov <dkirov@gmail.com>
|
||||||
|
## Travis Shirk <travis@pobox.com>
|
||||||
|
## Norman Rasmussen <norman@rasmussen.co.za>
|
||||||
|
## Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
random.seed()
|
||||||
|
|
||||||
|
import signal
|
||||||
|
if os.name != 'nt':
|
||||||
|
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||||
|
import getpass
|
||||||
|
import gobject
|
||||||
|
import notify
|
||||||
|
|
||||||
|
from common import helpers
|
||||||
|
from common import gajim
|
||||||
|
from common import GnuPG
|
||||||
|
from common.zeroconf import connection_handlers_zeroconf
|
||||||
|
from common.zeroconf import client_zeroconf
|
||||||
|
from connection_handlers_zeroconf import *
|
||||||
|
|
||||||
|
USE_GPG = GnuPG.USE_GPG
|
||||||
|
|
||||||
|
class ConnectionZeroconf(ConnectionHandlersZeroconf):
|
||||||
|
'''Connection class'''
|
||||||
|
def __init__(self, name):
|
||||||
|
ConnectionHandlersZeroconf.__init__(self)
|
||||||
|
# system username
|
||||||
|
self.username = None
|
||||||
|
self.name = name
|
||||||
|
self.connected = 0 # offline
|
||||||
|
self.connection = None
|
||||||
|
self.gpg = None
|
||||||
|
self.is_zeroconf = True
|
||||||
|
self.privacy_rules_supported = False
|
||||||
|
self.status = ''
|
||||||
|
self.old_show = ''
|
||||||
|
self.priority = 0
|
||||||
|
|
||||||
|
self.call_resolve_timeout = False
|
||||||
|
|
||||||
|
#self.time_to_reconnect = None
|
||||||
|
#self.new_account_info = None
|
||||||
|
self.bookmarks = []
|
||||||
|
|
||||||
|
#we don't need a password, but must be non-empty
|
||||||
|
self.password = 'zeroconf'
|
||||||
|
|
||||||
|
self.autoconnect = False
|
||||||
|
self.sync_with_global_status = True
|
||||||
|
self.no_log_for = False
|
||||||
|
|
||||||
|
# Do we continue connection when we get roster (send presence,get vcard...)
|
||||||
|
self.continue_connect_info = None
|
||||||
|
if USE_GPG:
|
||||||
|
self.gpg = GnuPG.GnuPG()
|
||||||
|
gajim.config.set('usegpg', True)
|
||||||
|
else:
|
||||||
|
gajim.config.set('usegpg', False)
|
||||||
|
|
||||||
|
self.get_config_values_or_default()
|
||||||
|
|
||||||
|
self.muc_jid = {} # jid of muc server for each transport type
|
||||||
|
self.vcard_supported = False
|
||||||
|
|
||||||
|
def _on_name_conflictCB(self, alt_name):
|
||||||
|
self.disconnect()
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
self.dispatch('ZC_NAME_CONFLICT', alt_name)
|
||||||
|
|
||||||
|
def get_config_values_or_default(self):
|
||||||
|
''' get name, host, port from config, or
|
||||||
|
create zeroconf account with default values'''
|
||||||
|
|
||||||
|
if not self.username:
|
||||||
|
self.username = unicode(getpass.getuser())
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name', self.username)
|
||||||
|
else:
|
||||||
|
self.username = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name')
|
||||||
|
|
||||||
|
if not gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name'):
|
||||||
|
print 'Creating zeroconf account'
|
||||||
|
gajim.config.add_per('accounts', gajim.ZEROCONF_ACC_NAME)
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'autoconnect', True)
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'no_log_for', '')
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'password', 'zeroconf')
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status', True)
|
||||||
|
|
||||||
|
#XXX make sure host is US-ASCII
|
||||||
|
self.host = unicode(socket.gethostname())
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', self.host)
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port', 5298)
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'is_zeroconf', True)
|
||||||
|
self.host = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname')
|
||||||
|
self.port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port')
|
||||||
|
self.autoconnect = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'autoconnect')
|
||||||
|
self.sync_with_global_status = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status')
|
||||||
|
self.no_log_for = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'no_log_for')
|
||||||
|
self.first = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_first_name')
|
||||||
|
self.last = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_last_name')
|
||||||
|
self.jabber_id = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_jabber_id')
|
||||||
|
self.email = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_email')
|
||||||
|
# END __init__
|
||||||
|
|
||||||
|
def dispatch(self, event, data):
|
||||||
|
if gajim.handlers.has_key(event):
|
||||||
|
gajim.handlers[event](self.name, data)
|
||||||
|
|
||||||
|
def _reconnect(self):
|
||||||
|
gajim.log.debug('reconnect')
|
||||||
|
|
||||||
|
signed = self.get_signed_msg(self.status)
|
||||||
|
self.reconnect()
|
||||||
|
|
||||||
|
def quit(self, kill_core):
|
||||||
|
if kill_core and self.connected > 1:
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def disable_account(self):
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def test_gpg_passphrase(self, password):
|
||||||
|
self.gpg.passphrase = password
|
||||||
|
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
||||||
|
signed = self.gpg.sign('test', keyID)
|
||||||
|
self.gpg.password = None
|
||||||
|
return signed != 'BAD_PASSPHRASE'
|
||||||
|
|
||||||
|
def get_signed_msg(self, msg):
|
||||||
|
signed = ''
|
||||||
|
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
||||||
|
if keyID and USE_GPG:
|
||||||
|
use_gpg_agent = gajim.config.get('use_gpg_agent')
|
||||||
|
if self.connected < 2 and self.gpg.passphrase is None and \
|
||||||
|
not use_gpg_agent:
|
||||||
|
# We didn't set a passphrase
|
||||||
|
self.dispatch('ERROR', (_('OpenPGP passphrase was not given'),
|
||||||
|
#%s is the account name here
|
||||||
|
_('You will be connected to %s without OpenPGP.') % self.name))
|
||||||
|
elif self.gpg.passphrase is not None or use_gpg_agent:
|
||||||
|
signed = self.gpg.sign(msg, keyID)
|
||||||
|
if signed == 'BAD_PASSPHRASE':
|
||||||
|
signed = ''
|
||||||
|
if self.connected < 2:
|
||||||
|
self.dispatch('BAD_PASSPHRASE', ())
|
||||||
|
return signed
|
||||||
|
|
||||||
|
def _on_resolve_timeout(self):
|
||||||
|
if self.connected:
|
||||||
|
self.connection.resolve_all()
|
||||||
|
diffs = self.roster.getDiffs()
|
||||||
|
for key in diffs:
|
||||||
|
self.roster.setItem(key)
|
||||||
|
self.dispatch('ROSTER_INFO', (key, self.roster.getName(key),
|
||||||
|
'both', 'no', self.roster.getGroups(key)))
|
||||||
|
self.dispatch('NOTIFY', (key, self.roster.getStatus(key),
|
||||||
|
self.roster.getMessage(key), 'local', 0, None, 0))
|
||||||
|
#XXX open chat windows don't get refreshed (full name), add that
|
||||||
|
return self.call_resolve_timeout
|
||||||
|
|
||||||
|
# callbacks called from zeroconf
|
||||||
|
def _on_new_service(self,jid):
|
||||||
|
self.roster.setItem(jid)
|
||||||
|
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
|
||||||
|
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0))
|
||||||
|
|
||||||
|
def _on_remove_service(self, jid):
|
||||||
|
self.roster.delItem(jid)
|
||||||
|
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
|
||||||
|
# keyID, timestamp))
|
||||||
|
self.dispatch('NOTIFY', (jid, 'offline', '', 'local', 0, None, 0))
|
||||||
|
|
||||||
|
def _on_disconnected(self):
|
||||||
|
self.disconnect()
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
self.dispatch('CONNECTION_LOST',
|
||||||
|
(_('Connection with account "%s" has been lost') % self.name,
|
||||||
|
_('To continue sending and receiving messages, you will need to reconnect.')))
|
||||||
|
self.status = 'offline'
|
||||||
|
self.disconnect()
|
||||||
|
|
||||||
|
def connect(self, show = 'online', msg = ''):
|
||||||
|
self.get_config_values_or_default()
|
||||||
|
if not self.connection:
|
||||||
|
self.connection = client_zeroconf.ClientZeroconf(self)
|
||||||
|
if not self.connection.test_avahi():
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
self.status = 'offline'
|
||||||
|
self.dispatch('CONNECTION_LOST',
|
||||||
|
(_('Could not connect to "%s"') % self.name,
|
||||||
|
_('Please check if Avahi is installed.')))
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
self.connection.connect(show, msg)
|
||||||
|
if not self.connection.listener:
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
self.status = 'offline'
|
||||||
|
self.dispatch('CONNECTION_LOST',
|
||||||
|
(_('Could not start local service'),
|
||||||
|
_('Please check if avahi-daemon is running.')))
|
||||||
|
self.disconnect()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.connection.announce()
|
||||||
|
self.roster = self.connection.getRoster()
|
||||||
|
self.dispatch('ROSTER', self.roster)
|
||||||
|
|
||||||
|
#display contacts already detected and resolved
|
||||||
|
for jid in self.roster.keys():
|
||||||
|
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
|
||||||
|
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0))
|
||||||
|
|
||||||
|
self.connected = STATUS_LIST.index(show)
|
||||||
|
|
||||||
|
# refresh all contacts data every five seconds
|
||||||
|
self.call_resolve_timeout = True
|
||||||
|
gobject.timeout_add(5000, self._on_resolve_timeout)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def disconnect(self, on_purpose = False):
|
||||||
|
self.connected = 0
|
||||||
|
self.time_to_reconnect = None
|
||||||
|
if self.connection:
|
||||||
|
self.connection.disconnect()
|
||||||
|
self.connection = None
|
||||||
|
# stop calling the timeout
|
||||||
|
self.call_resolve_timeout = False
|
||||||
|
|
||||||
|
def reannounce(self):
|
||||||
|
if self.connected:
|
||||||
|
txt = {}
|
||||||
|
txt['1st'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_first_name')
|
||||||
|
txt['last'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_last_name')
|
||||||
|
txt['jid'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_jabber_id')
|
||||||
|
txt['email'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_email')
|
||||||
|
self.connection.reannounce(txt)
|
||||||
|
|
||||||
|
def update_details(self):
|
||||||
|
if self.connection:
|
||||||
|
port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port')
|
||||||
|
if port != self.port:
|
||||||
|
self.port = port
|
||||||
|
last_msg = self.connection.last_msg
|
||||||
|
self.disconnect()
|
||||||
|
if not self.connect(self.status, last_msg):
|
||||||
|
return
|
||||||
|
if self.status != 'invisible':
|
||||||
|
self.connection.announce()
|
||||||
|
else:
|
||||||
|
self.reannounce()
|
||||||
|
|
||||||
|
def change_status(self, show, msg, sync = False, auto = False):
|
||||||
|
if not show in STATUS_LIST:
|
||||||
|
return -1
|
||||||
|
self.status = show
|
||||||
|
|
||||||
|
check = True #to check for errors from zeroconf
|
||||||
|
# 'connect'
|
||||||
|
if show != 'offline' and not self.connected:
|
||||||
|
if not self.connect(show, msg):
|
||||||
|
return
|
||||||
|
if show != 'invisible':
|
||||||
|
check = self.connection.announce()
|
||||||
|
else:
|
||||||
|
self.connected = STATUS_LIST.index(show)
|
||||||
|
|
||||||
|
# 'disconnect'
|
||||||
|
elif show == 'offline' and self.connected:
|
||||||
|
self.disconnect()
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
|
||||||
|
# update status
|
||||||
|
elif show != 'offline' and self.connected:
|
||||||
|
was_invisible = self.connected == STATUS_LIST.index('invisible')
|
||||||
|
self.connected = STATUS_LIST.index(show)
|
||||||
|
if show == 'invisible':
|
||||||
|
check = check and self.connection.remove_announce()
|
||||||
|
elif was_invisible:
|
||||||
|
check = check and self.connection.announce()
|
||||||
|
if self.connection and not show == 'invisible':
|
||||||
|
check = check and self.connection.set_show_msg(show, msg)
|
||||||
|
|
||||||
|
#stay offline when zeroconf does something wrong
|
||||||
|
if check:
|
||||||
|
self.dispatch('STATUS', show)
|
||||||
|
else:
|
||||||
|
# show notification that avahi, or system bus is down
|
||||||
|
self.dispatch('STATUS', 'offline')
|
||||||
|
self.status = 'offline'
|
||||||
|
self.dispatch('CONNECTION_LOST',
|
||||||
|
(_('Could not change status of account "%s"') % self.name,
|
||||||
|
_('Please check if avahi-daemon is running.')))
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
return STATUS_LIST[self.connected]
|
||||||
|
|
||||||
|
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
|
||||||
|
chatstate = None, msg_id = None, composing_jep = None, resource = None,
|
||||||
|
user_nick = None):
|
||||||
|
fjid = jid
|
||||||
|
|
||||||
|
if not self.connection:
|
||||||
|
return
|
||||||
|
if not msg and chatstate is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
msgtxt = msg
|
||||||
|
msgenc = ''
|
||||||
|
if keyID and USE_GPG:
|
||||||
|
#encrypt
|
||||||
|
msgenc = self.gpg.encrypt(msg, [keyID])
|
||||||
|
if msgenc:
|
||||||
|
msgtxt = '[This message is encrypted]'
|
||||||
|
lang = os.getenv('LANG')
|
||||||
|
if lang is not None or lang != 'en': # we're not english
|
||||||
|
msgtxt = _('[This message is encrypted]') +\
|
||||||
|
' ([This message is encrypted])' # one in locale and one en
|
||||||
|
|
||||||
|
|
||||||
|
if type == 'chat':
|
||||||
|
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if subject:
|
||||||
|
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
|
||||||
|
typ = 'normal', subject = subject)
|
||||||
|
else:
|
||||||
|
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
|
||||||
|
typ = 'normal')
|
||||||
|
|
||||||
|
if msgenc:
|
||||||
|
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
|
||||||
|
|
||||||
|
# chatstates - if peer supports jep85 or jep22, send chatstates
|
||||||
|
# please note that the only valid tag inside a message containing a <body>
|
||||||
|
# tag is the active event
|
||||||
|
if chatstate is not None:
|
||||||
|
if composing_jep == 'JEP-0085' or not composing_jep:
|
||||||
|
# JEP-0085
|
||||||
|
msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES)
|
||||||
|
if composing_jep == 'JEP-0022' or not composing_jep:
|
||||||
|
# JEP-0022
|
||||||
|
chatstate_node = msg_iq.setTag('x', namespace = common.xmpp.NS_EVENT)
|
||||||
|
if not msgtxt: # when no <body>, add <id>
|
||||||
|
if not msg_id: # avoid putting 'None' in <id> tag
|
||||||
|
msg_id = ''
|
||||||
|
chatstate_node.setTagData('id', msg_id)
|
||||||
|
# when msgtxt, requests JEP-0022 composing notification
|
||||||
|
if chatstate is 'composing' or msgtxt:
|
||||||
|
chatstate_node.addChild(name = 'composing')
|
||||||
|
|
||||||
|
self.connection.send(msg_iq)
|
||||||
|
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
|
||||||
|
ji = gajim.get_jid_without_resource(jid)
|
||||||
|
if self.name not in no_log_for and ji not in no_log_for:
|
||||||
|
log_msg = msg
|
||||||
|
if subject:
|
||||||
|
log_msg = _('Subject: %s\n%s') % (subject, msg)
|
||||||
|
if log_msg:
|
||||||
|
if type == 'chat':
|
||||||
|
kind = 'chat_msg_sent'
|
||||||
|
else:
|
||||||
|
kind = 'single_msg_sent'
|
||||||
|
gajim.logger.write(kind, jid, log_msg)
|
||||||
|
|
||||||
|
self.dispatch('MSGSENT', (jid, msg, keyID))
|
||||||
|
|
||||||
|
def send_stanza(self, stanza):
|
||||||
|
# send a stanza untouched
|
||||||
|
print 'connection_zeroconf.py: send_stanza'
|
||||||
|
if not self.connection:
|
||||||
|
return
|
||||||
|
#self.connection.send(stanza)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def ack_subscribed(self, jid):
|
||||||
|
gajim.log.debug('This should not happen (ack_subscribed)')
|
||||||
|
|
||||||
|
def ack_unsubscribed(self, jid):
|
||||||
|
gajim.log.debug('This should not happen (ack_unsubscribed)')
|
||||||
|
|
||||||
|
def request_subscription(self, jid, msg = '', name = '', groups = [],
|
||||||
|
auto_auth = False):
|
||||||
|
gajim.log.debug('This should not happen (request_subscription)')
|
||||||
|
|
||||||
|
def send_authorization(self, jid):
|
||||||
|
gajim.log.debug('This should not happen (send_authorization)')
|
||||||
|
|
||||||
|
def refuse_authorization(self, jid):
|
||||||
|
gajim.log.debug('This should not happen (refuse_authorization)')
|
||||||
|
|
||||||
|
def unsubscribe(self, jid, remove_auth = True):
|
||||||
|
gajim.log.debug('This should not happen (unsubscribe)')
|
||||||
|
|
||||||
|
def unsubscribe_agent(self, agent):
|
||||||
|
gajim.log.debug('This should not happen (unsubscribe_agent)')
|
||||||
|
|
||||||
|
def update_contact(self, jid, name, groups):
|
||||||
|
if self.connection:
|
||||||
|
self.connection.getRoster().setItem(jid = jid, name = name,
|
||||||
|
groups = groups)
|
||||||
|
|
||||||
|
def new_account(self, name, config, sync = False):
|
||||||
|
gajim.log.debug('This should not happen (new_account)')
|
||||||
|
|
||||||
|
def _on_new_account(self, con = None, con_type = None):
|
||||||
|
gajim.log.debug('This should not happen (_on_new_account)')
|
||||||
|
|
||||||
|
def account_changed(self, new_name):
|
||||||
|
self.name = new_name
|
||||||
|
|
||||||
|
def request_last_status_time(self, jid, resource):
|
||||||
|
gajim.log.debug('This should not happen (request_last_status_time)')
|
||||||
|
|
||||||
|
def request_os_info(self, jid, resource):
|
||||||
|
gajim.log.debug('This should not happen (request_os_info)')
|
||||||
|
|
||||||
|
def get_settings(self):
|
||||||
|
gajim.log.debug('This should not happen (get_settings)')
|
||||||
|
|
||||||
|
def get_bookmarks(self):
|
||||||
|
gajim.log.debug('This should not happen (get_bookmarks)')
|
||||||
|
|
||||||
|
def store_bookmarks(self):
|
||||||
|
gajim.log.debug('This should not happen (store_bookmarks)')
|
||||||
|
|
||||||
|
def get_metacontacts(self):
|
||||||
|
gajim.log.debug('This should not happen (get_metacontacts)')
|
||||||
|
|
||||||
|
def send_agent_status(self, agent, ptype):
|
||||||
|
gajim.log.debug('This should not happen (send_agent_status)')
|
||||||
|
|
||||||
|
def gpg_passphrase(self, passphrase):
|
||||||
|
if USE_GPG:
|
||||||
|
use_gpg_agent = gajim.config.get('use_gpg_agent')
|
||||||
|
if use_gpg_agent:
|
||||||
|
self.gpg.passphrase = None
|
||||||
|
else:
|
||||||
|
self.gpg.passphrase = passphrase
|
||||||
|
|
||||||
|
def ask_gpg_keys(self):
|
||||||
|
if USE_GPG:
|
||||||
|
keys = self.gpg.get_keys()
|
||||||
|
return keys
|
||||||
|
return None
|
||||||
|
|
||||||
|
def ask_gpg_secrete_keys(self):
|
||||||
|
if USE_GPG:
|
||||||
|
keys = self.gpg.get_secret_keys()
|
||||||
|
return keys
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _event_dispatcher(self, realm, event, data):
|
||||||
|
if realm == '':
|
||||||
|
if event == common.xmpp.transports.DATA_RECEIVED:
|
||||||
|
self.dispatch('STANZA_ARRIVED', unicode(data, errors = 'ignore'))
|
||||||
|
elif event == common.xmpp.transports.DATA_SENT:
|
||||||
|
self.dispatch('STANZA_SENT', unicode(data))
|
||||||
|
|
||||||
|
# END ConnectionZeroconf
|
|
@ -0,0 +1,152 @@
|
||||||
|
## common/zeroconf/roster_zeroconf.py
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
from common.zeroconf import zeroconf
|
||||||
|
|
||||||
|
class Roster:
|
||||||
|
def __init__(self, zeroconf):
|
||||||
|
self._data = None
|
||||||
|
self.zeroconf = zeroconf # our zeroconf instance
|
||||||
|
|
||||||
|
def update_roster(self):
|
||||||
|
for val in self.zeroconf.contacts.values():
|
||||||
|
self.setItem(val[zeroconf.C_NAME])
|
||||||
|
|
||||||
|
def getRoster(self):
|
||||||
|
#print 'roster_zeroconf.py: getRoster'
|
||||||
|
if self._data is None:
|
||||||
|
self._data = {}
|
||||||
|
self.update_roster()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def getDiffs(self):
|
||||||
|
''' update the roster with new data and return dict with
|
||||||
|
jid -> new status pairs to do notifications and stuff '''
|
||||||
|
|
||||||
|
diffs = {}
|
||||||
|
old_data = self._data.copy()
|
||||||
|
self.update_roster()
|
||||||
|
for key in old_data.keys():
|
||||||
|
if self._data.has_key(key):
|
||||||
|
if old_data[key] != self._data[key]:
|
||||||
|
diffs[key] = self._data[key]['status']
|
||||||
|
#print 'roster_zeroconf.py: diffs:' + str(diffs)
|
||||||
|
return diffs
|
||||||
|
|
||||||
|
def setItem(self, jid, name = '', groups = ''):
|
||||||
|
#print 'roster_zeroconf.py: setItem %s' % jid
|
||||||
|
(service_jid, domain, interface, protocol, host, address, port, bare_jid, txt) \
|
||||||
|
= self.zeroconf.get_contact(jid)
|
||||||
|
|
||||||
|
self._data[jid]={}
|
||||||
|
self._data[jid]['ask'] = 'no' #?
|
||||||
|
self._data[jid]['subscription'] = 'both'
|
||||||
|
self._data[jid]['groups'] = []
|
||||||
|
self._data[jid]['resources'] = {}
|
||||||
|
self._data[jid]['address'] = address
|
||||||
|
self._data[jid]['host'] = host
|
||||||
|
self._data[jid]['port'] = port
|
||||||
|
txt_dict = self.zeroconf.txt_array_to_dict(txt)
|
||||||
|
if txt_dict.has_key('status'):
|
||||||
|
status = txt_dict['status']
|
||||||
|
else:
|
||||||
|
status = ''
|
||||||
|
nm = ''
|
||||||
|
if txt_dict.has_key('1st'):
|
||||||
|
nm = txt_dict['1st']
|
||||||
|
if txt_dict.has_key('last'):
|
||||||
|
if nm != '':
|
||||||
|
nm += ' '
|
||||||
|
nm += txt_dict['last']
|
||||||
|
if nm:
|
||||||
|
self._data[jid]['name'] = nm
|
||||||
|
else:
|
||||||
|
self._data[jid]['name'] = jid
|
||||||
|
if status == 'avail':
|
||||||
|
status = 'online'
|
||||||
|
self._data[jid]['txt_dict'] = txt_dict
|
||||||
|
if not self._data[jid]['txt_dict'].has_key('msg'):
|
||||||
|
self._data[jid]['txt_dict']['msg'] = ''
|
||||||
|
self._data[jid]['status'] = status
|
||||||
|
self._data[jid]['show'] = status
|
||||||
|
|
||||||
|
def delItem(self, jid):
|
||||||
|
#print 'roster_zeroconf.py: delItem %s' % jid
|
||||||
|
if self._data.has_key(jid):
|
||||||
|
del self._data[jid]
|
||||||
|
|
||||||
|
def getItem(self, jid):
|
||||||
|
#print 'roster_zeroconf.py: getItem: %s' % jid
|
||||||
|
if self._data.has_key(jid):
|
||||||
|
return self._data[jid]
|
||||||
|
|
||||||
|
def __getitem__(self,jid):
|
||||||
|
#print 'roster_zeroconf.py: __getitem__'
|
||||||
|
return self._data[jid]
|
||||||
|
|
||||||
|
def getItems(self):
|
||||||
|
#print 'roster_zeroconf.py: getItems'
|
||||||
|
# Return list of all [bare] JIDs that the roster currently tracks.
|
||||||
|
return self._data.keys()
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
#print 'roster_zeroconf.py: keys'
|
||||||
|
return self._data.keys()
|
||||||
|
|
||||||
|
def getRaw(self):
|
||||||
|
#print 'roster_zeroconf.py: getRaw'
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
def getResources(self, jid):
|
||||||
|
#print 'roster_zeroconf.py: getResources(%s)' % jid
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def getGroups(self, jid):
|
||||||
|
return self._data[jid]['groups']
|
||||||
|
|
||||||
|
def getName(self, jid):
|
||||||
|
if self._data.has_key(jid):
|
||||||
|
return self._data[jid]['name']
|
||||||
|
|
||||||
|
def getStatus(self, jid):
|
||||||
|
if self._data.has_key(jid):
|
||||||
|
return self._data[jid]['status']
|
||||||
|
|
||||||
|
def getMessage(self, jid):
|
||||||
|
if self._data.has_key(jid):
|
||||||
|
return self._data[jid]['txt_dict']['msg']
|
||||||
|
|
||||||
|
def getShow(self, jid):
|
||||||
|
#print 'roster_zeroconf.py: getShow'
|
||||||
|
return getStatus(jid)
|
||||||
|
|
||||||
|
def getPriority(jid):
|
||||||
|
return 5
|
||||||
|
|
||||||
|
def getSubscription(self,jid):
|
||||||
|
#print 'roster_zeroconf.py: getSubscription'
|
||||||
|
return 'both'
|
||||||
|
|
||||||
|
def Subscribe(self,jid):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def Unsubscribe(self,jid):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def Authorize(self,jid):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def Unauthorize(self,jid):
|
||||||
|
pass
|
|
@ -0,0 +1,373 @@
|
||||||
|
## common/zeroconf/zeroconf.py
|
||||||
|
##
|
||||||
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
from common import gajim
|
||||||
|
from common import xmpp
|
||||||
|
|
||||||
|
try:
|
||||||
|
import avahi, gobject, dbus
|
||||||
|
except ImportError:
|
||||||
|
gajim.log.debug('Error: python-avahi and python-dbus need to be installed. No zeroconf support.')
|
||||||
|
|
||||||
|
try:
|
||||||
|
import dbus.glib
|
||||||
|
except ImportError, e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
C_NAME, C_DOMAIN, C_INTERFACE, C_PROTOCOL, C_HOST, \
|
||||||
|
C_ADDRESS, C_PORT, C_BARE_NAME, C_TXT = range(9)
|
||||||
|
|
||||||
|
class Zeroconf:
|
||||||
|
def __init__(self, new_serviceCB, remove_serviceCB, name_conflictCB,
|
||||||
|
disconnected_CB, name, host, port):
|
||||||
|
self.server = None
|
||||||
|
self.domain = None # specific domain to browse
|
||||||
|
self.stype = '_presence._tcp'
|
||||||
|
self.port = port # listening port that gets announced
|
||||||
|
self.username = name
|
||||||
|
self.host = host
|
||||||
|
self.txt = {} # service data
|
||||||
|
|
||||||
|
#XXX these CBs should be set to None when we destroy the object
|
||||||
|
# (go offline), because they create a circular reference
|
||||||
|
self.new_serviceCB = new_serviceCB
|
||||||
|
self.remove_serviceCB = remove_serviceCB
|
||||||
|
self.name_conflictCB = name_conflictCB
|
||||||
|
self.disconnected_CB = disconnected_CB
|
||||||
|
|
||||||
|
self.service_browser = None
|
||||||
|
self.domain_browser = None
|
||||||
|
self.server = None
|
||||||
|
self.contacts = {} # all current local contacts with data
|
||||||
|
self.entrygroup = None
|
||||||
|
self.connected = False
|
||||||
|
self.announced = False
|
||||||
|
self.invalid_self_contact = {}
|
||||||
|
|
||||||
|
|
||||||
|
## handlers for dbus callbacks
|
||||||
|
def entrygroup_commit_error_CB(self, err):
|
||||||
|
# left for eventual later use
|
||||||
|
pass
|
||||||
|
|
||||||
|
def error_callback1(self, err):
|
||||||
|
gajim.log.debug('RR' + str(err))
|
||||||
|
|
||||||
|
def error_callback(self, err):
|
||||||
|
gajim.log.debug(str(err))
|
||||||
|
# timeouts are non-critical
|
||||||
|
if str(err) != 'Timeout reached':
|
||||||
|
self.disconnect()
|
||||||
|
self.disconnected_CB()
|
||||||
|
|
||||||
|
def new_service_callback(self, interface, protocol, name, stype, domain, flags):
|
||||||
|
gajim.log.debug('Found service %s in domain %s on %i.%i.' % (name, domain, interface, protocol))
|
||||||
|
# if not self.connected:
|
||||||
|
# return
|
||||||
|
|
||||||
|
# synchronous resolving
|
||||||
|
self.server.ResolveService( int(interface), int(protocol), name, stype, \
|
||||||
|
domain, avahi.PROTO_UNSPEC, dbus.UInt32(0), \
|
||||||
|
reply_handler=self.service_resolved_callback, error_handler=self.error_callback1)
|
||||||
|
|
||||||
|
def remove_service_callback(self, interface, protocol, name, stype, domain, flags):
|
||||||
|
gajim.log.debug('Service %s in domain %s on %i.%i disappeared.' % (name, domain, interface, protocol))
|
||||||
|
# if not self.connected:
|
||||||
|
# return
|
||||||
|
if name != self.name:
|
||||||
|
for key in self.contacts.keys():
|
||||||
|
if self.contacts[key][C_BARE_NAME] == name:
|
||||||
|
del self.contacts[key]
|
||||||
|
self.remove_serviceCB(key)
|
||||||
|
return
|
||||||
|
|
||||||
|
def new_service_type(self, interface, protocol, stype, domain, flags):
|
||||||
|
# Are we already browsing this domain for this type?
|
||||||
|
if self.service_browser:
|
||||||
|
return
|
||||||
|
|
||||||
|
object_path = self.server.ServiceBrowserNew(interface, protocol, \
|
||||||
|
stype, domain, dbus.UInt32(0))
|
||||||
|
|
||||||
|
self.service_browser = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, \
|
||||||
|
object_path) , avahi.DBUS_INTERFACE_SERVICE_BROWSER)
|
||||||
|
self.service_browser.connect_to_signal('ItemNew', self.new_service_callback)
|
||||||
|
self.service_browser.connect_to_signal('ItemRemove', self.remove_service_callback)
|
||||||
|
self.service_browser.connect_to_signal('Failure', self.error_callback)
|
||||||
|
|
||||||
|
def new_domain_callback(self,interface, protocol, domain, flags):
|
||||||
|
if domain != "local":
|
||||||
|
self.browse_domain(interface, protocol, domain)
|
||||||
|
|
||||||
|
def txt_array_to_dict(self,txt_array):
|
||||||
|
items = {}
|
||||||
|
|
||||||
|
for byte_array in txt_array:
|
||||||
|
# 'str' is used for string type in python
|
||||||
|
value = avahi.byte_array_to_string(byte_array)
|
||||||
|
poseq = value.find('=')
|
||||||
|
items[value[:poseq]] = value[poseq+1:]
|
||||||
|
return items
|
||||||
|
|
||||||
|
def service_resolved_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
|
||||||
|
gajim.log.debug('Service data for service %s in domain %s on %i.%i:' % (name, domain, interface, protocol))
|
||||||
|
gajim.log.debug('Host %s (%s), port %i, TXT data: %s' % (host, address, port, avahi.txt_array_to_string_array(txt)))
|
||||||
|
if not self.connected:
|
||||||
|
return
|
||||||
|
bare_name = name
|
||||||
|
if name.find('@') == -1:
|
||||||
|
name = name + '@' + name
|
||||||
|
|
||||||
|
# we don't want to see ourselves in the list
|
||||||
|
if name != self.name:
|
||||||
|
self.contacts[name] = (name, domain, interface, protocol, host, address, port,
|
||||||
|
bare_name, txt)
|
||||||
|
self.new_serviceCB(name)
|
||||||
|
else:
|
||||||
|
# remember data
|
||||||
|
# In case this is not our own record but of another
|
||||||
|
# gajim instance on the same machine,
|
||||||
|
# it will be used when we get a new name.
|
||||||
|
self.invalid_self_contact[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
|
||||||
|
|
||||||
|
|
||||||
|
# different handler when resolving all contacts
|
||||||
|
def service_resolved_all_callback(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags):
|
||||||
|
if not self.connected:
|
||||||
|
return
|
||||||
|
bare_name = name
|
||||||
|
if name.find('@') == -1:
|
||||||
|
name = name + '@' + name
|
||||||
|
self.contacts[name] = (name, domain, interface, protocol, host, address, port, bare_name, txt)
|
||||||
|
|
||||||
|
def service_added_callback(self):
|
||||||
|
gajim.log.debug('Service successfully added')
|
||||||
|
|
||||||
|
def service_committed_callback(self):
|
||||||
|
gajim.log.debug('Service successfully committed')
|
||||||
|
|
||||||
|
def service_updated_callback(self):
|
||||||
|
gajim.log.debug('Service successfully updated')
|
||||||
|
|
||||||
|
def service_add_fail_callback(self, err):
|
||||||
|
gajim.log.debug('Error while adding service. %s' % str(err))
|
||||||
|
alternative_name = self.server.GetAlternativeServiceName(self.username)
|
||||||
|
self.disconnect()
|
||||||
|
self.name_conflictCB(alternative_name)
|
||||||
|
|
||||||
|
def server_state_changed_callback(self, state, error):
|
||||||
|
print 'server.state %s' % state
|
||||||
|
if state == avahi.SERVER_RUNNING:
|
||||||
|
self.create_service()
|
||||||
|
elif state == avahi.SERVER_COLLISION:
|
||||||
|
self.entrygroup.Reset()
|
||||||
|
elif state == avahi.CLIENT_FAILURE: # TODO: add error handling (avahi daemon dies...?)
|
||||||
|
print 'CLIENT FAILURE'
|
||||||
|
|
||||||
|
def entrygroup_state_changed_callback(self, state, error):
|
||||||
|
# the name is already present, so recreate
|
||||||
|
if state == avahi.ENTRY_GROUP_COLLISION:
|
||||||
|
self.service_add_fail_callback('Local name collision, recreating.')
|
||||||
|
elif state == avahi.ENTRY_GROUP_FAILURE:
|
||||||
|
print 'zeroconf.py: ENTRY_GROUP_FAILURE reached(that should not happen)'
|
||||||
|
|
||||||
|
# make zeroconf-valid names
|
||||||
|
def replace_show(self, show):
|
||||||
|
if show in ['chat', 'online', '']:
|
||||||
|
return 'avail'
|
||||||
|
elif show == 'xa':
|
||||||
|
return 'away'
|
||||||
|
return show
|
||||||
|
|
||||||
|
def create_service(self):
|
||||||
|
try:
|
||||||
|
if not self.entrygroup:
|
||||||
|
# create an EntryGroup for publishing
|
||||||
|
self.entrygroup = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.EntryGroupNew()), avahi.DBUS_INTERFACE_ENTRY_GROUP)
|
||||||
|
self.entrygroup.connect_to_signal('StateChanged', self.entrygroup_state_changed_callback)
|
||||||
|
|
||||||
|
txt = {}
|
||||||
|
|
||||||
|
#remove empty keys
|
||||||
|
for key,val in self.txt.iteritems():
|
||||||
|
if val:
|
||||||
|
txt[key] = val
|
||||||
|
|
||||||
|
txt['port.p2pj'] = self.port
|
||||||
|
txt['version'] = 1
|
||||||
|
txt['txtvers'] = 1
|
||||||
|
|
||||||
|
# replace gajim's show messages with compatible ones
|
||||||
|
if self.txt.has_key('status'):
|
||||||
|
txt['status'] = self.replace_show(self.txt['status'])
|
||||||
|
else:
|
||||||
|
txt['status'] = 'avail'
|
||||||
|
|
||||||
|
self.txt = txt
|
||||||
|
gajim.log.debug('Publishing service %s of type %s' % (self.name, self.stype))
|
||||||
|
self.entrygroup.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype, '', '', self.port, avahi.dict_to_txt_array(self.txt), reply_handler=self.service_added_callback, error_handler=self.service_add_fail_callback)
|
||||||
|
self.entrygroup.Commit(reply_handler=self.service_committed_callback,
|
||||||
|
error_handler=self.entrygroup_commit_error_CB)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except dbus.dbus_bindings.DBusException, e:
|
||||||
|
gajim.log.debug(str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def announce(self):
|
||||||
|
if not self.connected:
|
||||||
|
return False
|
||||||
|
|
||||||
|
state = self.server.GetState()
|
||||||
|
if state == avahi.SERVER_RUNNING:
|
||||||
|
self.create_service()
|
||||||
|
self.announced = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remove_announce(self):
|
||||||
|
if self.announced == False:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if self.entrygroup.GetState() != avahi.ENTRY_GROUP_FAILURE:
|
||||||
|
self.entrygroup.Reset()
|
||||||
|
self.entrygroup.Free()
|
||||||
|
self.entrygroup = None
|
||||||
|
self.announced = False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except dbus.dbus_bindings.DBusException, e:
|
||||||
|
gajim.log.debug("Can't remove service. That should not happen")
|
||||||
|
|
||||||
|
def browse_domain(self, interface, protocol, domain):
|
||||||
|
self.new_service_type(interface, protocol, self.stype, domain, '')
|
||||||
|
|
||||||
|
def avahi_dbus_connect_cb(self, a, connect, disconnect):
|
||||||
|
if connect != "":
|
||||||
|
gajim.log.debug('Lost connection to avahi-daemon')
|
||||||
|
try:
|
||||||
|
self.connected = False
|
||||||
|
self.disconnect()
|
||||||
|
self.disconnected_CB()
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
else:
|
||||||
|
gajim.log.debug('We are connected to avahi-daemon')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# connect to dbus
|
||||||
|
def connect_dbus(self):
|
||||||
|
if self.server:
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
self.bus = dbus.SystemBus()
|
||||||
|
self.bus.add_signal_receiver(self.avahi_dbus_connect_cb,
|
||||||
|
"NameOwnerChanged", "org.freedesktop.DBus",
|
||||||
|
arg0="org.freedesktop.Avahi")
|
||||||
|
self.server = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, \
|
||||||
|
avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER)
|
||||||
|
self.server.connect_to_signal('StateChanged',
|
||||||
|
self.server_state_changed_callback)
|
||||||
|
except Exception, e:
|
||||||
|
# Avahi service is not present
|
||||||
|
self.server = None
|
||||||
|
gajim.log.debug(str(e))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
self.name = self.username + '@' + self.host # service name
|
||||||
|
if not self.connect_dbus():
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.connected = True
|
||||||
|
# start browsing
|
||||||
|
if self.domain is None:
|
||||||
|
# Explicitly browse .local
|
||||||
|
self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "local")
|
||||||
|
|
||||||
|
# Browse for other browsable domains
|
||||||
|
self.domain_browser = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, \
|
||||||
|
self.server.DomainBrowserNew(avahi.IF_UNSPEC, \
|
||||||
|
avahi.PROTO_UNSPEC, '', avahi.DOMAIN_BROWSER_BROWSE,\
|
||||||
|
dbus.UInt32(0))), avahi.DBUS_INTERFACE_DOMAIN_BROWSER)
|
||||||
|
self.domain_browser.connect_to_signal('ItemNew', self.new_domain_callback)
|
||||||
|
self.domain_browser.connect_to_signal('Failure', self.error_callback)
|
||||||
|
else:
|
||||||
|
self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, domain)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
if self.connected:
|
||||||
|
self.connected = False
|
||||||
|
if self.service_browser:
|
||||||
|
self.service_browser.Free()
|
||||||
|
if self.domain_browser:
|
||||||
|
self.domain_browser.Free()
|
||||||
|
self.remove_announce()
|
||||||
|
self.service_browser = None
|
||||||
|
self.domain_browser = None
|
||||||
|
self.server = None
|
||||||
|
|
||||||
|
# refresh txt data of all contacts manually (no callback available)
|
||||||
|
def resolve_all(self):
|
||||||
|
for val in self.contacts.values():
|
||||||
|
self.server.ResolveService(int(val[C_INTERFACE]), int(val[C_PROTOCOL]), val[C_BARE_NAME], \
|
||||||
|
self.stype, val[C_DOMAIN], avahi.PROTO_UNSPEC, dbus.UInt32(0),\
|
||||||
|
reply_handler=self.service_resolved_all_callback, error_handler=self.error_callback)
|
||||||
|
|
||||||
|
def get_contacts(self):
|
||||||
|
return self.contacts
|
||||||
|
|
||||||
|
def get_contact(self, jid):
|
||||||
|
return self.contacts[jid]
|
||||||
|
|
||||||
|
def update_txt(self, show = None):
|
||||||
|
if show:
|
||||||
|
self.txt['status'] = self.replace_show(show)
|
||||||
|
|
||||||
|
txt = avahi.dict_to_txt_array(self.txt)
|
||||||
|
if self.connected and self.entrygroup:
|
||||||
|
self.entrygroup.UpdateServiceTxt(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), self.name, self.stype,'', txt, reply_handler=self.service_updated_callback, error_handler=self.error_callback)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# END Zeroconf
|
||||||
|
|
||||||
|
'''
|
||||||
|
# how to use
|
||||||
|
|
||||||
|
zeroconf = Zeroconf()
|
||||||
|
zeroconf.connect()
|
||||||
|
zeroconf.txt['1st'] = 'foo'
|
||||||
|
zeroconf.txt['last'] = 'bar'
|
||||||
|
zeroconf.txt['email'] = foo@bar.org
|
||||||
|
zeroconf.announce()
|
||||||
|
|
||||||
|
# updating after announcing
|
||||||
|
txt = {}
|
||||||
|
txt['status'] = 'avail'
|
||||||
|
txt['msg'] = 'Here I am'
|
||||||
|
zeroconf.update_txt(txt)
|
||||||
|
'''
|
330
src/config.py
330
src/config.py
|
@ -4,6 +4,7 @@
|
||||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
||||||
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
|
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
|
||||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
|
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
|
||||||
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## This program is free software; you can redistribute it and/or modify
|
||||||
## it under the terms of the GNU General Public License as published
|
## it under the terms of the GNU General Public License as published
|
||||||
|
@ -38,6 +39,7 @@ from common import helpers
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import connection
|
from common import connection
|
||||||
from common import passwords
|
from common import passwords
|
||||||
|
from common import zeroconf
|
||||||
from common import dbus_support
|
from common import dbus_support
|
||||||
|
|
||||||
from common.exceptions import GajimGeneralException
|
from common.exceptions import GajimGeneralException
|
||||||
|
@ -1374,6 +1376,9 @@ class AccountModificationWindow:
|
||||||
config['custom_host'] = self.xml.get_widget(
|
config['custom_host'] = self.xml.get_widget(
|
||||||
'custom_host_entry').get_text().decode('utf-8')
|
'custom_host_entry').get_text().decode('utf-8')
|
||||||
|
|
||||||
|
# update in case the name was changed to local account's name
|
||||||
|
config['is_zeroconf'] = False
|
||||||
|
|
||||||
config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().decode('utf-8')
|
config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().decode('utf-8')
|
||||||
if config['keyname'] == '': #no key selected
|
if config['keyname'] == '': #no key selected
|
||||||
config['keyid'] = ''
|
config['keyid'] = ''
|
||||||
|
@ -1808,6 +1813,22 @@ class AccountsWindow:
|
||||||
st = gajim.config.get('mergeaccounts')
|
st = gajim.config.get('mergeaccounts')
|
||||||
self.xml.get_widget('merge_checkbutton').set_active(st)
|
self.xml.get_widget('merge_checkbutton').set_active(st)
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
avahi_error = False
|
||||||
|
try:
|
||||||
|
import avahi
|
||||||
|
except ImportError:
|
||||||
|
avahi_error = True
|
||||||
|
|
||||||
|
# enable zeroconf
|
||||||
|
st = gajim.config.get('enable_zeroconf')
|
||||||
|
w = self.xml.get_widget('enable_zeroconf_checkbutton')
|
||||||
|
w.set_active(st)
|
||||||
|
if os.name == 'nt' or (avahi_error and not w.get_active()):
|
||||||
|
w.set_sensitive(False)
|
||||||
|
self.zeroconf_toggled_id = w.connect('toggled', self.on_enable_zeroconf_checkbutton_toggled)
|
||||||
|
|
||||||
def on_accounts_window_key_press_event(self, widget, event):
|
def on_accounts_window_key_press_event(self, widget, event):
|
||||||
if event.keyval == gtk.keysyms.Escape:
|
if event.keyval == gtk.keysyms.Escape:
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
@ -1847,6 +1868,17 @@ class AccountsWindow:
|
||||||
dialogs.ErrorDialog(_('Unread events'),
|
dialogs.ErrorDialog(_('Unread events'),
|
||||||
_('Read all pending events before removing this account.'))
|
_('Read all pending events before removing this account.'))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if gajim.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
|
w = self.xml.get_widget('enable_zeroconf_checkbutton')
|
||||||
|
w.set_active(False)
|
||||||
|
else:
|
||||||
|
if gajim.interface.instances[account].has_key('remove_account'):
|
||||||
|
gajim.interface.instances[account]['remove_account'].window.present()
|
||||||
|
else:
|
||||||
|
gajim.interface.instances[account]['remove_account'] = \
|
||||||
|
RemoveAccountWindow(account)
|
||||||
|
|
||||||
win_opened = False
|
win_opened = False
|
||||||
if gajim.interface.msg_win_mgr.get_controls(acct = account):
|
if gajim.interface.msg_win_mgr.get_controls(acct = account):
|
||||||
win_opened = True
|
win_opened = True
|
||||||
|
@ -1891,21 +1923,107 @@ class AccountsWindow:
|
||||||
self.show_modification_window(account)
|
self.show_modification_window(account)
|
||||||
|
|
||||||
def show_modification_window(self, account):
|
def show_modification_window(self, account):
|
||||||
if gajim.interface.instances[account].has_key('account_modification'):
|
if gajim.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
gajim.interface.instances[account]['account_modification'].window.present()
|
if gajim.interface.instances.has_key('zeroconf_properties'):
|
||||||
|
gajim.interface.instances['zeroconf_properties'].window.present()
|
||||||
|
else:
|
||||||
|
gajim.interface.instances['zeroconf_properties'] = \
|
||||||
|
ZeroconfPropertiesWindow()
|
||||||
else:
|
else:
|
||||||
gajim.interface.instances[account]['account_modification'] = \
|
if gajim.interface.instances[account].has_key('account_modification'):
|
||||||
AccountModificationWindow(account)
|
gajim.interface.instances[account]['account_modification'].window.present()
|
||||||
|
else:
|
||||||
|
gajim.interface.instances[account]['account_modification'] = \
|
||||||
|
AccountModificationWindow(account)
|
||||||
|
|
||||||
|
def on_checkbutton_toggled(self, widget, config_name,
|
||||||
|
change_sensitivity_widgets = None):
|
||||||
|
gajim.config.set(config_name, widget.get_active())
|
||||||
|
if change_sensitivity_widgets:
|
||||||
|
for w in change_sensitivity_widgets:
|
||||||
|
w.set_sensitive(widget.get_active())
|
||||||
|
gajim.interface.save_config()
|
||||||
|
|
||||||
def on_merge_checkbutton_toggled(self, widget):
|
def on_merge_checkbutton_toggled(self, widget):
|
||||||
gajim.config.set('mergeaccounts', widget.get_active())
|
self.on_checkbutton_toggled(widget, 'mergeaccounts')
|
||||||
gajim.interface.save_config()
|
|
||||||
if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
|
if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
|
||||||
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
|
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
|
||||||
else:
|
else:
|
||||||
gajim.interface.roster.regroup = False
|
gajim.interface.roster.regroup = False
|
||||||
gajim.interface.roster.draw_roster()
|
gajim.interface.roster.draw_roster()
|
||||||
|
|
||||||
|
|
||||||
|
def on_enable_zeroconf_checkbutton_toggled(self, widget):
|
||||||
|
# don't do anything if there is an account with the local name but is a normal account
|
||||||
|
if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME) and not gajim.connections[gajim.ZEROCONF_ACC_NAME].is_zeroconf:
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME].dispatch('ERROR', (_('Account Local already exists.'),_('Please rename or remove it before enabling link-local messaging.')))
|
||||||
|
widget.disconnect(self.zeroconf_toggled_id)
|
||||||
|
widget.set_active(False)
|
||||||
|
self.zeroconf_toggled_id = widget.connect('toggled', self.on_enable_zeroconf_checkbutton_toggled)
|
||||||
|
return
|
||||||
|
|
||||||
|
if gajim.config.get('enable_zeroconf'):
|
||||||
|
#disable
|
||||||
|
gajim.interface.roster.close_all(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME].disable_account()
|
||||||
|
del gajim.connections[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
gajim.interface.save_config()
|
||||||
|
del gajim.interface.instances[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.nicks[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.block_signed_in_notifications[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.groups[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
gajim.contacts.remove_account(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
del gajim.gc_connected[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.automatic_rooms[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.to_be_removed[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.newly_added[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.sleeper_state[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.encrypted_chats[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.last_message_time[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
del gajim.status_before_autoaway[gajim.ZEROCONF_ACC_NAME]
|
||||||
|
if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
|
||||||
|
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
|
||||||
|
else:
|
||||||
|
gajim.interface.roster.regroup = False
|
||||||
|
gajim.interface.roster.draw_roster()
|
||||||
|
gajim.interface.roster.actions_menu_needs_rebuild = True
|
||||||
|
if gajim.interface.instances.has_key('accounts'):
|
||||||
|
gajim.interface.instances['accounts'].init_accounts()
|
||||||
|
|
||||||
|
else:
|
||||||
|
# enable (will create new account if not present)
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME] = common.zeroconf.connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
# update variables
|
||||||
|
gajim.interface.instances[gajim.ZEROCONF_ACC_NAME] = {'infos': {}, 'disco': {},
|
||||||
|
'gc_config': {}}
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME].connected = 0
|
||||||
|
gajim.groups[gajim.ZEROCONF_ACC_NAME] = {}
|
||||||
|
gajim.contacts.add_account(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
gajim.gc_connected[gajim.ZEROCONF_ACC_NAME] = {}
|
||||||
|
gajim.automatic_rooms[gajim.ZEROCONF_ACC_NAME] = {}
|
||||||
|
gajim.newly_added[gajim.ZEROCONF_ACC_NAME] = []
|
||||||
|
gajim.to_be_removed[gajim.ZEROCONF_ACC_NAME] = []
|
||||||
|
gajim.nicks[gajim.ZEROCONF_ACC_NAME] = gajim.ZEROCONF_ACC_NAME
|
||||||
|
gajim.block_signed_in_notifications[gajim.ZEROCONF_ACC_NAME] = True
|
||||||
|
gajim.sleeper_state[gajim.ZEROCONF_ACC_NAME] = 'off'
|
||||||
|
gajim.encrypted_chats[gajim.ZEROCONF_ACC_NAME] = []
|
||||||
|
gajim.last_message_time[gajim.ZEROCONF_ACC_NAME] = {}
|
||||||
|
gajim.status_before_autoaway[gajim.ZEROCONF_ACC_NAME] = ''
|
||||||
|
# refresh accounts window
|
||||||
|
if gajim.interface.instances.has_key('accounts'):
|
||||||
|
gajim.interface.instances['accounts'].init_accounts()
|
||||||
|
# refresh roster
|
||||||
|
if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
|
||||||
|
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
|
||||||
|
else:
|
||||||
|
gajim.interface.roster.regroup = False
|
||||||
|
gajim.interface.roster.draw_roster()
|
||||||
|
gajim.interface.roster.actions_menu_needs_rebuild = True
|
||||||
|
gajim.interface.save_config()
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME].change_status('online', '')
|
||||||
|
|
||||||
|
self.on_checkbutton_toggled(widget, 'enable_zeroconf')
|
||||||
|
|
||||||
class DataFormWindow:
|
class DataFormWindow:
|
||||||
def __init__(self, account, config):
|
def __init__(self, account, config):
|
||||||
self.account = account
|
self.account = account
|
||||||
|
@ -3034,3 +3152,203 @@ _('You can set advanced account options by pressing Advanced button, or later by
|
||||||
gajim.interface.roster.draw_roster()
|
gajim.interface.roster.draw_roster()
|
||||||
gajim.interface.roster.actions_menu_needs_rebuild = True
|
gajim.interface.roster.actions_menu_needs_rebuild = True
|
||||||
gajim.interface.save_config()
|
gajim.interface.save_config()
|
||||||
|
|
||||||
|
#---------- ZeroconfPropertiesWindow class -------------#
|
||||||
|
class ZeroconfPropertiesWindow:
|
||||||
|
def __init__(self):
|
||||||
|
self.xml = gtkgui_helpers.get_glade('zeroconf_properties_window.glade')
|
||||||
|
self.window = self.xml.get_widget('zeroconf_properties_window')
|
||||||
|
self.window.set_transient_for(gajim.interface.roster.window)
|
||||||
|
self.xml.signal_autoconnect(self)
|
||||||
|
|
||||||
|
self.init_account()
|
||||||
|
self.init_account_gpg()
|
||||||
|
|
||||||
|
self.xml.get_widget('save_button').grab_focus()
|
||||||
|
self.window.show_all()
|
||||||
|
|
||||||
|
def init_account(self):
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'autoconnect')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('autoconnect_checkbutton').set_active(st)
|
||||||
|
|
||||||
|
list_no_log_for = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,'no_log_for').split()
|
||||||
|
if gajim.ZEROCONF_ACC_NAME in list_no_log_for:
|
||||||
|
self.xml.get_widget('log_history_checkbutton').set_active(0)
|
||||||
|
else:
|
||||||
|
self.xml.get_widget('log_history_checkbutton').set_active(1)
|
||||||
|
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('sync_with_global_status_checkbutton').set_active(st)
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_first_name')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('first_name_entry').set_text(st)
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_last_name')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('last_name_entry').set_text(st)
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_jabber_id')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('jabber_id_entry').set_text(st)
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'zeroconf_email')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('email_entry').set_text(st)
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('custom_port_entry').set_text(str(st))
|
||||||
|
|
||||||
|
st = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'use_custom_host')
|
||||||
|
if st:
|
||||||
|
self.xml.get_widget('custom_port_checkbutton').set_active(st)
|
||||||
|
|
||||||
|
self.xml.get_widget('custom_port_entry').set_sensitive(bool(st))
|
||||||
|
|
||||||
|
if not st:
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port', '5298')
|
||||||
|
|
||||||
|
def init_account_gpg(self):
|
||||||
|
keyid = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'keyid')
|
||||||
|
keyname = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'keyname')
|
||||||
|
savegpgpass = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME,'savegpgpass')
|
||||||
|
|
||||||
|
if not keyid or not gajim.config.get('usegpg'):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.xml.get_widget('gpg_key_label').set_text(keyid)
|
||||||
|
self.xml.get_widget('gpg_name_label').set_text(keyname)
|
||||||
|
gpg_save_password_checkbutton = \
|
||||||
|
self.xml.get_widget('gpg_save_password_checkbutton')
|
||||||
|
gpg_save_password_checkbutton.set_sensitive(True)
|
||||||
|
gpg_save_password_checkbutton.set_active(savegpgpass)
|
||||||
|
|
||||||
|
if savegpgpass:
|
||||||
|
entry = self.xml.get_widget('gpg_password_entry')
|
||||||
|
entry.set_sensitive(True)
|
||||||
|
gpgpassword = gajim.config.get_per('accounts',
|
||||||
|
gajim.ZEROCONF_ACC_NAME, 'gpgpassword')
|
||||||
|
entry.set_text(gpgpassword)
|
||||||
|
|
||||||
|
def on_zeroconf_properties_window_destroy(self, widget):
|
||||||
|
#close window
|
||||||
|
if gajim.interface.instances.has_key('zeroconf_properties'):
|
||||||
|
del gajim.interface.instances['zeroconf_properties']
|
||||||
|
|
||||||
|
def on_custom_port_checkbutton_toggled(self, widget):
|
||||||
|
st = self.xml.get_widget('custom_port_checkbutton').get_active()
|
||||||
|
self.xml.get_widget('custom_port_entry').set_sensitive(bool(st))
|
||||||
|
|
||||||
|
def on_cancel_button_clicked(self, widget):
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def on_save_button_clicked(self, widget):
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
st = self.xml.get_widget('autoconnect_checkbutton').get_active()
|
||||||
|
config['autoconnect'] = st
|
||||||
|
list_no_log_for = gajim.config.get_per('accounts',
|
||||||
|
gajim.ZEROCONF_ACC_NAME, 'no_log_for').split()
|
||||||
|
if gajim.ZEROCONF_ACC_NAME in list_no_log_for:
|
||||||
|
list_no_log_for.remove(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
if not self.xml.get_widget('log_history_checkbutton').get_active():
|
||||||
|
list_no_log_for.append(gajim.ZEROCONF_ACC_NAME)
|
||||||
|
config['no_log_for'] = ' '.join(list_no_log_for)
|
||||||
|
|
||||||
|
st = self.xml.get_widget('sync_with_global_status_checkbutton').get_active()
|
||||||
|
config['sync_with_global_status'] = st
|
||||||
|
|
||||||
|
st = self.xml.get_widget('first_name_entry').get_text()
|
||||||
|
config['zeroconf_first_name'] = st.decode('utf-8')
|
||||||
|
|
||||||
|
st = self.xml.get_widget('last_name_entry').get_text()
|
||||||
|
config['zeroconf_last_name'] = st.decode('utf-8')
|
||||||
|
|
||||||
|
st = self.xml.get_widget('jabber_id_entry').get_text()
|
||||||
|
config['zeroconf_jabber_id'] = st.decode('utf-8')
|
||||||
|
|
||||||
|
st = self.xml.get_widget('email_entry').get_text()
|
||||||
|
config['zeroconf_email'] = st.decode('utf-8')
|
||||||
|
|
||||||
|
use_custom_port = self.xml.get_widget('custom_port_checkbutton').get_active()
|
||||||
|
config['use_custom_host'] = use_custom_port
|
||||||
|
|
||||||
|
old_port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'custom_port')
|
||||||
|
if use_custom_port:
|
||||||
|
port = self.xml.get_widget('custom_port_entry').get_text()
|
||||||
|
else:
|
||||||
|
port = 5298
|
||||||
|
|
||||||
|
config['custom_port'] = port
|
||||||
|
|
||||||
|
config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().decode('utf-8')
|
||||||
|
if config['keyname'] == '': #no key selected
|
||||||
|
config['keyid'] = ''
|
||||||
|
config['savegpgpass'] = False
|
||||||
|
config['gpgpassword'] = ''
|
||||||
|
else:
|
||||||
|
config['keyid'] = self.xml.get_widget('gpg_key_label').get_text().decode('utf-8')
|
||||||
|
config['savegpgpass'] = self.xml.get_widget(
|
||||||
|
'gpg_save_password_checkbutton').get_active()
|
||||||
|
config['gpgpassword'] = self.xml.get_widget('gpg_password_entry'
|
||||||
|
).get_text().decode('utf-8')
|
||||||
|
|
||||||
|
reconnect = False
|
||||||
|
for opt in ('zeroconf_first_name','zeroconf_last_name', 'zeroconf_jabber_id', 'zeroconf_email', 'custom_port'):
|
||||||
|
if gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, opt) != config[opt]:
|
||||||
|
reconnect = True
|
||||||
|
|
||||||
|
for opt in config:
|
||||||
|
gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, opt, config[opt])
|
||||||
|
|
||||||
|
if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME):
|
||||||
|
if port != old_port or reconnect:
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME].update_details()
|
||||||
|
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def on_gpg_choose_button_clicked(self, widget, data = None):
|
||||||
|
if gajim.connections.has_key(gajim.ZEROCONF_ACC_NAME):
|
||||||
|
secret_keys = gajim.connections[gajim.ZEROCONF_ACC_NAME].ask_gpg_secrete_keys()
|
||||||
|
|
||||||
|
# self.account is None and/or gajim.connections is {}
|
||||||
|
else:
|
||||||
|
from common import GnuPG
|
||||||
|
if GnuPG.USE_GPG:
|
||||||
|
secret_keys = GnuPG.GnuPG().get_secret_keys()
|
||||||
|
else:
|
||||||
|
secret_keys = []
|
||||||
|
if not secret_keys:
|
||||||
|
dialogs.ErrorDialog(_('Failed to get secret keys'),
|
||||||
|
_('There was a problem retrieving your OpenPGP secret keys.'))
|
||||||
|
return
|
||||||
|
secret_keys[_('None')] = _('None')
|
||||||
|
instance = dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
|
||||||
|
_('Choose your OpenPGP key'), secret_keys)
|
||||||
|
keyID = instance.run()
|
||||||
|
if keyID is None:
|
||||||
|
return
|
||||||
|
checkbutton = self.xml.get_widget('gpg_save_password_checkbutton')
|
||||||
|
gpg_key_label = self.xml.get_widget('gpg_key_label')
|
||||||
|
gpg_name_label = self.xml.get_widget('gpg_name_label')
|
||||||
|
if keyID[0] == _('None'):
|
||||||
|
gpg_key_label.set_text(_('No key selected'))
|
||||||
|
gpg_name_label.set_text('')
|
||||||
|
checkbutton.set_sensitive(False)
|
||||||
|
self.xml.get_widget('gpg_password_entry').set_sensitive(False)
|
||||||
|
else:
|
||||||
|
gpg_key_label.set_text(keyID[0])
|
||||||
|
gpg_name_label.set_text(keyID[1])
|
||||||
|
checkbutton.set_sensitive(True)
|
||||||
|
checkbutton.set_active(False)
|
||||||
|
self.xml.get_widget('gpg_password_entry').set_text('')
|
||||||
|
|
||||||
|
def on_gpg_save_password_checkbutton_toggled(self, widget):
|
||||||
|
st = widget.get_active()
|
||||||
|
w = self.xml.get_widget('gpg_password_entry')
|
||||||
|
w.set_sensitive(bool(st))
|
||||||
|
# w.set_text = ''
|
||||||
|
|
28
src/gajim.py
28
src/gajim.py
|
@ -31,6 +31,7 @@ import message_control
|
||||||
from chat_control import ChatControlBase
|
from chat_control import ChatControlBase
|
||||||
|
|
||||||
from common import exceptions
|
from common import exceptions
|
||||||
|
from common.zeroconf import connection_zeroconf
|
||||||
|
|
||||||
if os.name == 'posix': # dl module is Unix Only
|
if os.name == 'posix': # dl module is Unix Only
|
||||||
try: # rename the process name to gajim
|
try: # rename the process name to gajim
|
||||||
|
@ -618,7 +619,9 @@ class Interface:
|
||||||
jids = full_jid_with_resource.split('/', 1)
|
jids = full_jid_with_resource.split('/', 1)
|
||||||
jid = jids[0]
|
jid = jids[0]
|
||||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
gc_control = self.msg_win_mgr.get_control(jid, account)
|
||||||
if gc_control and gc_control.type_id == message_control.TYPE_GC:
|
if gc_control and gc_control.type_id != message_control.TYPE_GC:
|
||||||
|
gc_control = None
|
||||||
|
if gc_control:
|
||||||
if len(jids) > 1: # it's a pm
|
if len(jids) > 1: # it's a pm
|
||||||
nick = jids[1]
|
nick = jids[1]
|
||||||
if not self.msg_win_mgr.get_control(full_jid_with_resource,
|
if not self.msg_win_mgr.get_control(full_jid_with_resource,
|
||||||
|
@ -1406,6 +1409,20 @@ class Interface:
|
||||||
if win.startswith('privacy_list_'):
|
if win.startswith('privacy_list_'):
|
||||||
self.instances[account][win].check_active_default(data)
|
self.instances[account][win].check_active_default(data)
|
||||||
|
|
||||||
|
def handle_event_zc_name_conflict(self, account, data):
|
||||||
|
dlg = dialogs.InputDialog(_('Username Conflict'),
|
||||||
|
_('Please type a new username for your local account'),
|
||||||
|
is_modal = True)
|
||||||
|
dlg.input_entry.set_text(data)
|
||||||
|
response = dlg.get_response()
|
||||||
|
if response == gtk.RESPONSE_OK:
|
||||||
|
new_name = dlg.input_entry.get_text()
|
||||||
|
gajim.config.set_per('accounts', account, 'name', new_name)
|
||||||
|
status = gajim.connections[account].status
|
||||||
|
gajim.connections[account].username = new_name
|
||||||
|
gajim.connections[account].change_status(status, '')
|
||||||
|
|
||||||
|
|
||||||
def read_sleepy(self):
|
def read_sleepy(self):
|
||||||
'''Check idle status and change that status if needed'''
|
'''Check idle status and change that status if needed'''
|
||||||
if not self.sleeper.poll():
|
if not self.sleeper.poll():
|
||||||
|
@ -1711,6 +1728,7 @@ class Interface:
|
||||||
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
|
'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
|
||||||
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
|
'PRIVACY_LISTS_ACTIVE_DEFAULT': \
|
||||||
self.handle_event_privacy_lists_active_default,
|
self.handle_event_privacy_lists_active_default,
|
||||||
|
'ZC_NAME_CONFLICT': self.handle_event_zc_name_conflict,
|
||||||
}
|
}
|
||||||
gajim.handlers = self.handlers
|
gajim.handlers = self.handlers
|
||||||
|
|
||||||
|
@ -1867,9 +1885,13 @@ class Interface:
|
||||||
self.handle_event_file_progress)
|
self.handle_event_file_progress)
|
||||||
gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
|
gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
|
||||||
self.register_handlers()
|
self.register_handlers()
|
||||||
|
if gajim.config.get('enable_zeroconf'):
|
||||||
|
gajim.connections[gajim.ZEROCONF_ACC_NAME] = common.zeroconf.connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
|
||||||
for account in gajim.config.get_per('accounts'):
|
for account in gajim.config.get_per('accounts'):
|
||||||
gajim.connections[account] = common.connection.Connection(account)
|
if not gajim.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
|
gajim.connections[account] = common.connection.Connection(account)
|
||||||
|
|
||||||
|
# gtk hooks
|
||||||
# gtk hooks
|
# gtk hooks
|
||||||
gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
|
gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
|
||||||
gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
|
gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
|
||||||
|
|
|
@ -1119,6 +1119,14 @@ class RosterWindow:
|
||||||
else:
|
else:
|
||||||
info[contact.jid] = vcard.VcardWindow(contact, account)
|
info[contact.jid] = vcard.VcardWindow(contact, account)
|
||||||
|
|
||||||
|
def on_info_zeroconf(self, widget, contact, account):
|
||||||
|
info = gajim.interface.instances[account]['infos']
|
||||||
|
if info.has_key(contact.jid):
|
||||||
|
info[contact.jid].window.present()
|
||||||
|
else:
|
||||||
|
info[contact.jid] = vcard.ZeroconfVcardWindow(contact, account)
|
||||||
|
|
||||||
|
|
||||||
def show_tooltip(self, contact):
|
def show_tooltip(self, contact):
|
||||||
pointer = self.tree.get_pointer()
|
pointer = self.tree.get_pointer()
|
||||||
props = self.tree.get_path_at_pos(pointer[0], pointer[1])
|
props = self.tree.get_path_at_pos(pointer[0], pointer[1])
|
||||||
|
@ -1363,6 +1371,118 @@ class RosterWindow:
|
||||||
if not contact:
|
if not contact:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if gajim.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
|
xml = gtkgui_helpers.get_glade('zeroconf_contact_context_menu.glade')
|
||||||
|
zeroconf_contact_context_menu = xml.get_widget('zeroconf_contact_context_menu')
|
||||||
|
|
||||||
|
start_chat_menuitem = xml.get_widget('start_chat_menuitem')
|
||||||
|
rename_menuitem = xml.get_widget('rename_menuitem')
|
||||||
|
edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
|
||||||
|
# separator has with send file, assign_openpgp_key_menuitem, etc..
|
||||||
|
above_send_file_separator = xml.get_widget('above_send_file_separator')
|
||||||
|
send_file_menuitem = xml.get_widget('send_file_menuitem')
|
||||||
|
assign_openpgp_key_menuitem = xml.get_widget(
|
||||||
|
'assign_openpgp_key_menuitem')
|
||||||
|
add_special_notification_menuitem = xml.get_widget(
|
||||||
|
'add_special_notification_menuitem')
|
||||||
|
|
||||||
|
add_special_notification_menuitem.hide()
|
||||||
|
add_special_notification_menuitem.set_no_show_all(True)
|
||||||
|
|
||||||
|
if not our_jid:
|
||||||
|
# add a special img for rename menuitem
|
||||||
|
path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
|
||||||
|
'kbd_input.png')
|
||||||
|
img = gtk.Image()
|
||||||
|
img.set_from_file(path_to_kbd_input_img)
|
||||||
|
rename_menuitem.set_image(img)
|
||||||
|
|
||||||
|
above_information_separator = xml.get_widget(
|
||||||
|
'above_information_separator')
|
||||||
|
|
||||||
|
# skip a separator
|
||||||
|
information_menuitem = xml.get_widget('information_menuitem')
|
||||||
|
history_menuitem = xml.get_widget('history_menuitem')
|
||||||
|
|
||||||
|
contacts = gajim.contacts.get_contact(account, jid)
|
||||||
|
if len(contacts) > 1: # sevral resources
|
||||||
|
sub_menu = gtk.Menu()
|
||||||
|
start_chat_menuitem.set_submenu(sub_menu)
|
||||||
|
|
||||||
|
iconset = gajim.config.get('iconset')
|
||||||
|
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
|
||||||
|
for c in contacts:
|
||||||
|
# icon MUST be different instance for every item
|
||||||
|
state_images = self.load_iconset(path)
|
||||||
|
item = gtk.ImageMenuItem(c.resource + ' (' + str(c.priority) + ')')
|
||||||
|
icon_name = helpers.get_icon_name_to_show(c, account)
|
||||||
|
icon = state_images[icon_name]
|
||||||
|
item.set_image(icon)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.on_open_chat_window, c, account,
|
||||||
|
c.resource)
|
||||||
|
|
||||||
|
else: # one resource
|
||||||
|
start_chat_menuitem.connect('activate',
|
||||||
|
self.on_roster_treeview_row_activated, tree_path)
|
||||||
|
|
||||||
|
if contact.resource:
|
||||||
|
send_file_menuitem.connect('activate',
|
||||||
|
self.on_send_file_menuitem_activate, account, contact)
|
||||||
|
else: # if we do not have resource we cannot send file
|
||||||
|
send_file_menuitem.hide()
|
||||||
|
send_file_menuitem.set_no_show_all(True)
|
||||||
|
|
||||||
|
rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
|
||||||
|
information_menuitem.connect('activate', self.on_info_zeroconf, contact,
|
||||||
|
account)
|
||||||
|
history_menuitem.connect('activate', self.on_history, contact,
|
||||||
|
account)
|
||||||
|
|
||||||
|
if _('Not in Roster') not in contact.groups:
|
||||||
|
#contact is in normal group
|
||||||
|
edit_groups_menuitem.set_no_show_all(False)
|
||||||
|
assign_openpgp_key_menuitem.set_no_show_all(False)
|
||||||
|
edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
|
||||||
|
contact,account)])
|
||||||
|
|
||||||
|
if gajim.config.get('usegpg'):
|
||||||
|
assign_openpgp_key_menuitem.connect('activate',
|
||||||
|
self.on_assign_pgp_key, contact, account)
|
||||||
|
|
||||||
|
else: # contact is in group 'Not in Roster'
|
||||||
|
edit_groups_menuitem.hide()
|
||||||
|
edit_groups_menuitem.set_no_show_all(True)
|
||||||
|
# hide first of the two consecutive separators
|
||||||
|
above_send_file_separator.hide()
|
||||||
|
above_send_file_separator.set_no_show_all(True)
|
||||||
|
assign_openpgp_key_menuitem.hide()
|
||||||
|
assign_openpgp_key_menuitem.set_no_show_all(True)
|
||||||
|
|
||||||
|
# Remove many items when it's self contact row
|
||||||
|
if our_jid:
|
||||||
|
for menuitem in (rename_menuitem, edit_groups_menuitem,
|
||||||
|
above_information_separator):
|
||||||
|
menuitem.set_no_show_all(True)
|
||||||
|
menuitem.hide()
|
||||||
|
|
||||||
|
# Unsensitive many items when account is offline
|
||||||
|
if gajim.connections[account].connected < 2:
|
||||||
|
for widget in [start_chat_menuitem, rename_menuitem, edit_groups_menuitem, send_file_menuitem]:
|
||||||
|
widget.set_sensitive(False)
|
||||||
|
|
||||||
|
event_button = gtkgui_helpers.get_possible_button_event(event)
|
||||||
|
|
||||||
|
zeroconf_contact_context_menu.attach_to_widget(self.tree, None)
|
||||||
|
zeroconf_contact_context_menu.connect('selection-done',
|
||||||
|
gtkgui_helpers.destroy_widget)
|
||||||
|
zeroconf_contact_context_menu.show_all()
|
||||||
|
zeroconf_contact_context_menu.popup(None, None, None, event_button,
|
||||||
|
event.time)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# normal account
|
||||||
xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
|
xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
|
||||||
roster_contact_context_menu = xml.get_widget(
|
roster_contact_context_menu = xml.get_widget(
|
||||||
'roster_contact_context_menu')
|
'roster_contact_context_menu')
|
||||||
|
@ -1773,6 +1893,14 @@ class RosterWindow:
|
||||||
gajim.interface.instances[account]['account_modification'] = \
|
gajim.interface.instances[account]['account_modification'] = \
|
||||||
config.AccountModificationWindow(account)
|
config.AccountModificationWindow(account)
|
||||||
|
|
||||||
|
def on_zeroconf_properties(self, widget, account):
|
||||||
|
if gajim.interface.instances.has_key('zeroconf_properties'):
|
||||||
|
gajim.interface.instances['zeroconf_properties'].\
|
||||||
|
window.present()
|
||||||
|
else:
|
||||||
|
gajim.interface.instances['zeroconf_properties'] = \
|
||||||
|
config.ZeroconfPropertiesWindow()
|
||||||
|
|
||||||
def on_open_gmail_inbox(self, widget, account):
|
def on_open_gmail_inbox(self, widget, account):
|
||||||
url = 'http://mail.google.com/mail?account_id=%s' % urllib.quote(
|
url = 'http://mail.google.com/mail?account_id=%s' % urllib.quote(
|
||||||
gajim.config.get_per('accounts', account, 'name'))
|
gajim.config.get_per('accounts', account, 'name'))
|
||||||
|
@ -1792,71 +1920,123 @@ class RosterWindow:
|
||||||
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
|
path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
|
||||||
state_images = self.load_iconset(path)
|
state_images = self.load_iconset(path)
|
||||||
|
|
||||||
xml = gtkgui_helpers.get_glade('account_context_menu.glade')
|
if not gajim.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
account_context_menu = xml.get_widget('account_context_menu')
|
xml = gtkgui_helpers.get_glade('account_context_menu.glade')
|
||||||
|
account_context_menu = xml.get_widget('account_context_menu')
|
||||||
|
|
||||||
status_menuitem = xml.get_widget('status_menuitem')
|
status_menuitem = xml.get_widget('status_menuitem')
|
||||||
join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
|
join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
|
||||||
open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem')
|
open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem')
|
||||||
new_message_menuitem = xml.get_widget('new_message_menuitem')
|
new_message_menuitem = xml.get_widget('new_message_menuitem')
|
||||||
add_contact_menuitem = xml.get_widget('add_contact_menuitem')
|
add_contact_menuitem = xml.get_widget('add_contact_menuitem')
|
||||||
service_discovery_menuitem = xml.get_widget('service_discovery_menuitem')
|
service_discovery_menuitem = xml.get_widget('service_discovery_menuitem')
|
||||||
edit_account_menuitem = xml.get_widget('edit_account_menuitem')
|
edit_account_menuitem = xml.get_widget('edit_account_menuitem')
|
||||||
sub_menu = gtk.Menu()
|
sub_menu = gtk.Menu()
|
||||||
status_menuitem.set_submenu(sub_menu)
|
status_menuitem.set_submenu(sub_menu)
|
||||||
|
|
||||||
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
|
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
|
||||||
uf_show = helpers.get_uf_show(show, use_mnemonic = True)
|
uf_show = helpers.get_uf_show(show, use_mnemonic = True)
|
||||||
|
item = gtk.ImageMenuItem(uf_show)
|
||||||
|
icon = state_images[show]
|
||||||
|
item.set_image(icon)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.change_status, account, show)
|
||||||
|
|
||||||
|
item = gtk.SeparatorMenuItem()
|
||||||
|
sub_menu.append(item)
|
||||||
|
|
||||||
|
item = gtk.ImageMenuItem(_('_Change Status Message'))
|
||||||
|
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
|
||||||
|
img = gtk.Image()
|
||||||
|
img.set_from_file(path)
|
||||||
|
item.set_image(img)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.on_change_status_message_activate, account)
|
||||||
|
if gajim.connections[account].connected < 2:
|
||||||
|
item.set_sensitive(False)
|
||||||
|
|
||||||
|
uf_show = helpers.get_uf_show('offline', use_mnemonic = True)
|
||||||
item = gtk.ImageMenuItem(uf_show)
|
item = gtk.ImageMenuItem(uf_show)
|
||||||
icon = state_images[show]
|
icon = state_images['offline']
|
||||||
item.set_image(icon)
|
item.set_image(icon)
|
||||||
sub_menu.append(item)
|
sub_menu.append(item)
|
||||||
item.connect('activate', self.change_status, account, show)
|
item.connect('activate', self.change_status, account, 'offline')
|
||||||
|
|
||||||
item = gtk.SeparatorMenuItem()
|
if gajim.config.get_per('accounts', account, 'hostname') not in gajim.gmail_domains:
|
||||||
sub_menu.append(item)
|
open_gmail_inbox_menuitem.set_no_show_all(True)
|
||||||
|
open_gmail_inbox_menuitem.hide()
|
||||||
|
else:
|
||||||
|
open_gmail_inbox_menuitem.connect('activate', self.on_open_gmail_inbox,
|
||||||
|
account)
|
||||||
|
|
||||||
item = gtk.ImageMenuItem(_('_Change Status Message'))
|
edit_account_menuitem.connect('activate', self.on_edit_account, account)
|
||||||
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
|
add_contact_menuitem.connect('activate', self.on_add_new_contact, account)
|
||||||
img = gtk.Image()
|
service_discovery_menuitem.connect('activate',
|
||||||
img.set_from_file(path)
|
self.on_service_disco_menuitem_activate, account)
|
||||||
item.set_image(img)
|
|
||||||
sub_menu.append(item)
|
gc_sub_menu = gtk.Menu() # gc is always a submenu
|
||||||
item.connect('activate', self.on_change_status_message_activate, account)
|
join_group_chat_menuitem.set_submenu(gc_sub_menu)
|
||||||
if gajim.connections[account].connected < 2:
|
self.add_bookmarks_list(gc_sub_menu, account)
|
||||||
item.set_sensitive(False)
|
new_message_menuitem.connect('activate',
|
||||||
|
self.on_new_message_menuitem_activate, account)
|
||||||
|
|
||||||
uf_show = helpers.get_uf_show('offline', use_mnemonic = True)
|
# make some items insensitive if account is offline
|
||||||
item = gtk.ImageMenuItem(uf_show)
|
if gajim.connections[account].connected < 2:
|
||||||
icon = state_images['offline']
|
for widget in [add_contact_menuitem, service_discovery_menuitem,
|
||||||
item.set_image(icon)
|
join_group_chat_menuitem, new_message_menuitem]:
|
||||||
sub_menu.append(item)
|
widget.set_sensitive(False)
|
||||||
item.connect('activate', self.change_status, account, 'offline')
|
|
||||||
|
|
||||||
if gajim.config.get_per('accounts', account, 'hostname') not in gajim.gmail_domains:
|
|
||||||
open_gmail_inbox_menuitem.set_no_show_all(True)
|
|
||||||
open_gmail_inbox_menuitem.hide()
|
|
||||||
else:
|
else:
|
||||||
open_gmail_inbox_menuitem.connect('activate', self.on_open_gmail_inbox,
|
xml = gtkgui_helpers.get_glade('zeroconf_context_menu.glade')
|
||||||
account)
|
account_context_menu = xml.get_widget('zeroconf_context_menu')
|
||||||
|
|
||||||
edit_account_menuitem.connect('activate', self.on_edit_account, account)
|
status_menuitem = xml.get_widget('status_menuitem')
|
||||||
add_contact_menuitem.connect('activate', self.on_add_new_contact, account)
|
#join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
|
||||||
service_discovery_menuitem.connect('activate',
|
new_message_menuitem = xml.get_widget('new_message_menuitem')
|
||||||
self.on_service_disco_menuitem_activate, account)
|
zeroconf_properties_menuitem = xml.get_widget('zeroconf_properties_menuitem')
|
||||||
|
sub_menu = gtk.Menu()
|
||||||
gc_sub_menu = gtk.Menu() # gc is always a submenu
|
status_menuitem.set_submenu(sub_menu)
|
||||||
join_group_chat_menuitem.set_submenu(gc_sub_menu)
|
|
||||||
self.add_bookmarks_list(gc_sub_menu, account)
|
|
||||||
new_message_menuitem.connect('activate',
|
|
||||||
self.on_new_message_menuitem_activate, account)
|
|
||||||
|
|
||||||
# make some items insensitive if account is offline
|
for show in ('online', 'away', 'dnd', 'invisible'):
|
||||||
if gajim.connections[account].connected < 2:
|
uf_show = helpers.get_uf_show(show, use_mnemonic = True)
|
||||||
for widget in [add_contact_menuitem, service_discovery_menuitem,
|
item = gtk.ImageMenuItem(uf_show)
|
||||||
join_group_chat_menuitem, new_message_menuitem]:
|
icon = state_images[show]
|
||||||
widget.set_sensitive(False)
|
item.set_image(icon)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.change_status, account, show)
|
||||||
|
|
||||||
|
item = gtk.SeparatorMenuItem()
|
||||||
|
sub_menu.append(item)
|
||||||
|
|
||||||
|
item = gtk.ImageMenuItem(_('_Change Status Message'))
|
||||||
|
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
|
||||||
|
img = gtk.Image()
|
||||||
|
img.set_from_file(path)
|
||||||
|
item.set_image(img)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.on_change_status_message_activate, account)
|
||||||
|
if gajim.connections[account].connected < 2:
|
||||||
|
item.set_sensitive(False)
|
||||||
|
|
||||||
|
uf_show = helpers.get_uf_show('offline', use_mnemonic = True)
|
||||||
|
item = gtk.ImageMenuItem(uf_show)
|
||||||
|
icon = state_images['offline']
|
||||||
|
item.set_image(icon)
|
||||||
|
sub_menu.append(item)
|
||||||
|
item.connect('activate', self.change_status, account, 'offline')
|
||||||
|
|
||||||
|
zeroconf_properties_menuitem.connect('activate', self.on_zeroconf_properties, account)
|
||||||
|
#gc_sub_menu = gtk.Menu() # gc is always a submenu
|
||||||
|
#join_group_chat_menuitem.set_submenu(gc_sub_menu)
|
||||||
|
#self.add_bookmarks_list(gc_sub_menu, account)
|
||||||
|
#new_message_menuitem.connect('activate',
|
||||||
|
# self.on_new_message_menuitem_activate, account)
|
||||||
|
|
||||||
|
# make some items insensitive if account is offline
|
||||||
|
#if gajim.connections[account].connected < 2:
|
||||||
|
# for widget in [join_group_chat_menuitem, new_message_menuitem]:
|
||||||
|
# widget.set_sensitive(False)
|
||||||
|
# new_message_menuitem.set_sensitive(False)
|
||||||
|
|
||||||
return account_context_menu
|
return account_context_menu
|
||||||
|
|
||||||
def make_account_menu(self, event, iter):
|
def make_account_menu(self, event, iter):
|
||||||
|
@ -1967,6 +2147,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
|
||||||
if not len(list_of_paths):
|
if not len(list_of_paths):
|
||||||
return
|
return
|
||||||
type = model[list_of_paths[0]][C_TYPE]
|
type = model[list_of_paths[0]][C_TYPE]
|
||||||
|
account = model[list_of_paths[0]][C_ACCOUNT]
|
||||||
list_ = []
|
list_ = []
|
||||||
for path in list_of_paths:
|
for path in list_of_paths:
|
||||||
if model[path][C_TYPE] != type:
|
if model[path][C_TYPE] != type:
|
||||||
|
@ -1976,7 +2157,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
|
||||||
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
contact = gajim.contacts.get_contact_with_highest_priority(account,
|
||||||
jid)
|
jid)
|
||||||
list_.append((contact, account))
|
list_.append((contact, account))
|
||||||
if type in ('account', 'group', 'self_contact'):
|
if type in ('account', 'group', 'self_contact') or account == gajim.ZEROCONF_ACC_NAME:
|
||||||
return
|
return
|
||||||
if type == 'contact':
|
if type == 'contact':
|
||||||
self.on_req_usub(widget, list_)
|
self.on_req_usub(widget, list_)
|
||||||
|
@ -3572,6 +3753,10 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
|
||||||
account_dest, c_dest, path)
|
account_dest, c_dest, path)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'):
|
||||||
|
# drop on zeroconf account, no contact adds possible
|
||||||
|
return
|
||||||
|
|
||||||
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
|
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
|
||||||
# dropped before a group : we drop it in the previous group
|
# dropped before a group : we drop it in the previous group
|
||||||
path_dest = (path_dest[0], path_dest[1]-1)
|
path_dest = (path_dest[0], path_dest[1]-1)
|
||||||
|
@ -3583,6 +3768,8 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
|
||||||
return
|
return
|
||||||
if type_dest == 'account' and account_source == account_dest:
|
if type_dest == 'account' and account_source == account_dest:
|
||||||
return
|
return
|
||||||
|
if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
|
||||||
|
return
|
||||||
it = iter_source
|
it = iter_source
|
||||||
while model[it][C_TYPE] == 'contact':
|
while model[it][C_TYPE] == 'contact':
|
||||||
it = model.iter_parent(it)
|
it = model.iter_parent(it)
|
||||||
|
|
|
@ -307,7 +307,14 @@ class GCTooltip(BaseTooltip):
|
||||||
properties.append((show, None))
|
properties.append((show, None))
|
||||||
|
|
||||||
if contact.jid.strip() != '':
|
if contact.jid.strip() != '':
|
||||||
properties.append((_('Jabber ID: '), contact.jid))
|
jid_markup = '<span weight="bold">' + contact.jid + '</span>'
|
||||||
|
else:
|
||||||
|
jid_markup = '<span weight="bold">' + \
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(contact.get_shown_name()) \
|
||||||
|
+ '</span>'
|
||||||
|
properties.append((jid_markup, None))
|
||||||
|
properties.append((_('Role: '), helpers.get_uf_role(contact.role)))
|
||||||
|
properties.append((_('Affiliation: '), contact.affiliation.capitalize()))
|
||||||
if hasattr(contact, 'resource') and contact.resource.strip() != '':
|
if hasattr(contact, 'resource') and contact.resource.strip() != '':
|
||||||
properties.append((_('Resource: '),
|
properties.append((_('Resource: '),
|
||||||
gtkgui_helpers.escape_for_pango_markup(contact.resource) ))
|
gtkgui_helpers.escape_for_pango_markup(contact.resource) ))
|
||||||
|
@ -408,10 +415,25 @@ class RosterTooltip(NotificationAreaTooltip):
|
||||||
vcard_table.set_homogeneous(False)
|
vcard_table.set_homogeneous(False)
|
||||||
vcard_current_row = 1
|
vcard_current_row = 1
|
||||||
properties = []
|
properties = []
|
||||||
name_markup = '<b>%s</b>' % gtkgui_helpers.escape_for_pango_markup(
|
|
||||||
prim_contact.get_shown_name())
|
|
||||||
properties.append((name_markup, None))
|
|
||||||
|
|
||||||
|
jid_markup = '<span weight="bold">' + prim_contact.jid + '</span>'
|
||||||
|
properties.append((jid_markup, None))
|
||||||
|
|
||||||
|
properties.append((_('Name: '), gtkgui_helpers.escape_for_pango_markup(
|
||||||
|
prim_contact.get_shown_name())))
|
||||||
|
if prim_contact.sub:
|
||||||
|
properties.append(( _('Subscription: '),
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(helpers.get_uf_sub(prim_contact.sub))))
|
||||||
|
if prim_contact.keyID:
|
||||||
|
keyID = None
|
||||||
|
if len(prim_contact.keyID) == 8:
|
||||||
|
keyID = prim_contact.keyID
|
||||||
|
elif len(prim_contact.keyID) == 16:
|
||||||
|
keyID = prim_contact.keyID[8:]
|
||||||
|
if keyID:
|
||||||
|
properties.append((_('OpenPGP: '),
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(keyID)))
|
||||||
|
|
||||||
num_resources = 0
|
num_resources = 0
|
||||||
# put contacts in dict, where key is priority
|
# put contacts in dict, where key is priority
|
||||||
contacts_dict = {}
|
contacts_dict = {}
|
||||||
|
@ -422,6 +444,11 @@ class RosterTooltip(NotificationAreaTooltip):
|
||||||
contacts_dict[contact.priority].append(contact)
|
contacts_dict[contact.priority].append(contact)
|
||||||
else:
|
else:
|
||||||
contacts_dict[contact.priority] = [contact]
|
contacts_dict[contact.priority] = [contact]
|
||||||
|
|
||||||
|
if num_resources == 1 and contact.resource:
|
||||||
|
properties.append((_('Resource: '),
|
||||||
|
gtkgui_helpers.escape_for_pango_markup(contact.resource) + ' (' + \
|
||||||
|
unicode(contact.priority) + ')'))
|
||||||
if num_resources > 1:
|
if num_resources > 1:
|
||||||
properties.append((_('Status: '), ' '))
|
properties.append((_('Status: '), ' '))
|
||||||
transport = gajim.get_transport_name_from_jid(
|
transport = gajim.get_transport_name_from_jid(
|
||||||
|
|
148
src/vcard.py
148
src/vcard.py
|
@ -2,6 +2,7 @@
|
||||||
##
|
##
|
||||||
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
|
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
|
||||||
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
|
||||||
|
## Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
|
||||||
##
|
##
|
||||||
## This program is free software; you can redistribute it and/or modify
|
## This program is free software; you can redistribute it and/or modify
|
||||||
## it under the terms of the GNU General Public License as published
|
## it under the terms of the GNU General Public License as published
|
||||||
|
@ -336,3 +337,150 @@ class VcardWindow:
|
||||||
|
|
||||||
def on_close_button_clicked(self, widget):
|
def on_close_button_clicked(self, widget):
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class ZeroconfVcardWindow:
|
||||||
|
def __init__(self, contact, account, is_fake = False):
|
||||||
|
# the contact variable is the jid if vcard is true
|
||||||
|
self.xml = gtkgui_helpers.get_glade('zeroconf_information_window.glade')
|
||||||
|
self.window = self.xml.get_widget('zeroconf_information_window')
|
||||||
|
|
||||||
|
self.contact = contact
|
||||||
|
self.account = account
|
||||||
|
self.is_fake = is_fake
|
||||||
|
|
||||||
|
# self.avatar_mime_type = None
|
||||||
|
# self.avatar_encoded = None
|
||||||
|
|
||||||
|
self.fill_contact_page()
|
||||||
|
self.fill_personal_page()
|
||||||
|
|
||||||
|
self.xml.signal_autoconnect(self)
|
||||||
|
self.window.show_all()
|
||||||
|
|
||||||
|
def on_zeroconf_information_window_destroy(self, widget):
|
||||||
|
del gajim.interface.instances[self.account]['infos'][self.contact.jid]
|
||||||
|
|
||||||
|
def on_zeroconf_information_window_key_press_event(self, widget, event):
|
||||||
|
if event.keyval == gtk.keysyms.Escape:
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def on_log_history_checkbutton_toggled(self, widget):
|
||||||
|
#log conversation history?
|
||||||
|
oldlog = True
|
||||||
|
no_log_for = gajim.config.get_per('accounts', self.account,
|
||||||
|
'no_log_for').split()
|
||||||
|
if self.contact.jid in no_log_for:
|
||||||
|
oldlog = False
|
||||||
|
log = widget.get_active()
|
||||||
|
if not log and not self.contact.jid in no_log_for:
|
||||||
|
no_log_for.append(self.contact.jid)
|
||||||
|
if log and self.contact.jid in no_log_for:
|
||||||
|
no_log_for.remove(self.contact.jid)
|
||||||
|
if oldlog != log:
|
||||||
|
gajim.config.set_per('accounts', self.account, 'no_log_for',
|
||||||
|
' '.join(no_log_for))
|
||||||
|
|
||||||
|
def on_PHOTO_eventbox_button_press_event(self, widget, event):
|
||||||
|
'''If right-clicked, show popup'''
|
||||||
|
if event.button == 3: # right click
|
||||||
|
menu = gtk.Menu()
|
||||||
|
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||||
|
menuitem.connect('activate',
|
||||||
|
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
||||||
|
self.contact.jid, self.account, self.contact.name + '.jpeg')
|
||||||
|
menu.append(menuitem)
|
||||||
|
menu.connect('selection-done', lambda w:w.destroy())
|
||||||
|
# show the menu
|
||||||
|
menu.show_all()
|
||||||
|
menu.popup(None, None, None, event.button, event.time)
|
||||||
|
|
||||||
|
def set_value(self, entry_name, value):
|
||||||
|
try:
|
||||||
|
if value and entry_name == 'URL_label':
|
||||||
|
if gtk.pygtk_version >= (2, 10, 0) and gtk.gtk_version >= (2, 10, 0):
|
||||||
|
widget = gtk.LinkButton(value, value)
|
||||||
|
else:
|
||||||
|
widget = gtk.Label(value)
|
||||||
|
table = self.xml.get_widget('personal_info_table')
|
||||||
|
table.attach(widget, 1, 4, 3, 4, yoptions = 0)
|
||||||
|
else:
|
||||||
|
self.xml.get_widget(entry_name).set_text(value)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def fill_status_label(self):
|
||||||
|
if self.xml.get_widget('information_notebook').get_n_pages() < 2:
|
||||||
|
return
|
||||||
|
contact_list = gajim.contacts.get_contact(self.account, self.contact.jid)
|
||||||
|
# stats holds show and status message
|
||||||
|
stats = ''
|
||||||
|
one = True # Are we adding the first line ?
|
||||||
|
if contact_list:
|
||||||
|
for c in contact_list:
|
||||||
|
if not one:
|
||||||
|
stats += '\n'
|
||||||
|
stats += helpers.get_uf_show(c.show)
|
||||||
|
if c.status:
|
||||||
|
stats += ': ' + c.status
|
||||||
|
if c.last_status_time:
|
||||||
|
stats += '\n' + _('since %s') % time.strftime('%c',
|
||||||
|
c.last_status_time).decode(locale.getpreferredencoding())
|
||||||
|
one = False
|
||||||
|
else: # Maybe gc_vcard ?
|
||||||
|
stats = helpers.get_uf_show(self.contact.show)
|
||||||
|
if self.contact.status:
|
||||||
|
stats += ': ' + self.contact.status
|
||||||
|
status_label = self.xml.get_widget('status_label')
|
||||||
|
status_label.set_max_width_chars(15)
|
||||||
|
status_label.set_text(stats)
|
||||||
|
|
||||||
|
tip = gtk.Tooltips()
|
||||||
|
status_label_eventbox = self.xml.get_widget('status_label_eventbox')
|
||||||
|
tip.set_tip(status_label_eventbox, stats)
|
||||||
|
|
||||||
|
def fill_contact_page(self):
|
||||||
|
tooltips = gtk.Tooltips()
|
||||||
|
self.xml.get_widget('nickname_label').set_markup(
|
||||||
|
'<b><span size="x-large">' +
|
||||||
|
self.contact.get_shown_name() +
|
||||||
|
'</span></b>')
|
||||||
|
self.xml.get_widget('local_jid_label').set_text(self.contact.jid)
|
||||||
|
|
||||||
|
log = True
|
||||||
|
if self.contact.jid in gajim.config.get_per('accounts', self.account,
|
||||||
|
'no_log_for').split(' '):
|
||||||
|
log = False
|
||||||
|
checkbutton = self.xml.get_widget('log_history_checkbutton')
|
||||||
|
checkbutton.set_active(log)
|
||||||
|
checkbutton.connect('toggled', self.on_log_history_checkbutton_toggled)
|
||||||
|
|
||||||
|
resources = '%s (%s)' % (self.contact.resource, unicode(
|
||||||
|
self.contact.priority))
|
||||||
|
uf_resources = self.contact.resource + _(' resource with priority ')\
|
||||||
|
+ unicode(self.contact.priority)
|
||||||
|
if not self.contact.status:
|
||||||
|
self.contact.status = ''
|
||||||
|
|
||||||
|
# Request list time status
|
||||||
|
# gajim.connections[self.account].request_last_status_time(self.contact.jid,
|
||||||
|
# self.contact.resource)
|
||||||
|
|
||||||
|
self.xml.get_widget('resource_prio_label').set_text(resources)
|
||||||
|
resource_prio_label_eventbox = self.xml.get_widget(
|
||||||
|
'resource_prio_label_eventbox')
|
||||||
|
tooltips.set_tip(resource_prio_label_eventbox, uf_resources)
|
||||||
|
|
||||||
|
self.fill_status_label()
|
||||||
|
|
||||||
|
# gajim.connections[self.account].request_vcard(self.contact.jid, self.is_fake)
|
||||||
|
|
||||||
|
def fill_personal_page(self):
|
||||||
|
contact = gajim.connections[gajim.ZEROCONF_ACC_NAME].roster.getItem(self.contact.jid)
|
||||||
|
self.xml.get_widget('first_name_label').set_text(contact['txt_dict']['1st'])
|
||||||
|
self.xml.get_widget('last_name_label').set_text(contact['txt_dict']['last'])
|
||||||
|
self.xml.get_widget('jabber_id_label').set_text(contact['txt_dict']['jid'])
|
||||||
|
self.xml.get_widget('email_label').set_text(contact['txt_dict']['email'])
|
||||||
|
|
||||||
|
def on_close_button_clicked(self, widget):
|
||||||
|
self.window.destroy()
|
||||||
|
|
Loading…
Reference in New Issue