diff --git a/data/glade/accounts_window.glade b/data/glade/accounts_window.glade
index 3ac6f9e7b34ba678959237e111471dd9dc3754ca..39b637bae74d41d517ee9693a0bc8e876d4c6c44 100644
--- a/data/glade/accounts_window.glade
+++ b/data/glade/accounts_window.glade
@@ -2,6 +2,7 @@
 <!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
 
 <glade-interface>
+
 <widget class="GtkWindow" id="accounts_window">
   <property name="border_width">12</property>
   <property name="title" translatable="yes">Accounts</property>
@@ -64,7 +65,7 @@
 	  <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="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="relief">GTK_RELIEF_NORMAL</property>
 	  <property name="focus_on_click">True</property>
@@ -80,6 +81,38 @@
 	</packing>
       </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>
 	<widget class="GtkHButtonBox" id="hbuttonbox15">
 	  <property name="visible">True</property>
@@ -267,4 +300,5 @@
     </widget>
   </child>
 </widget>
+
 </glade-interface>
diff --git a/data/glade/zeroconf_contact_context_menu.glade b/data/glade/zeroconf_contact_context_menu.glade
new file mode 100644
index 0000000000000000000000000000000000000000..edbb0f064bf9986d1744516ff30d236b596b98fe
--- /dev/null
+++ b/data/glade/zeroconf_contact_context_menu.glade
@@ -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>
diff --git a/data/glade/zeroconf_context_menu.glade b/data/glade/zeroconf_context_menu.glade
new file mode 100644
index 0000000000000000000000000000000000000000..f2928cf04811f9b139a02cf6e2c44737fcd54101
--- /dev/null
+++ b/data/glade/zeroconf_context_menu.glade
@@ -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>
diff --git a/data/glade/zeroconf_information_window.glade b/data/glade/zeroconf_information_window.glade
new file mode 100644
index 0000000000000000000000000000000000000000..8cdf7e8915455c2a55dd364cd7046c1b95d390dd
--- /dev/null
+++ b/data/glade/zeroconf_information_window.glade
@@ -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>
diff --git a/data/glade/zeroconf_properties_window.glade b/data/glade/zeroconf_properties_window.glade
new file mode 100644
index 0000000000000000000000000000000000000000..c296fe4d581ec83417c015d458cf539745fdfc99
--- /dev/null
+++ b/data/glade/zeroconf_properties_window.glade
@@ -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">&lt;b&gt;Personal Information&lt;/b&gt;</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">&lt;b&gt;OpenPGP&lt;/b&gt;</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>
diff --git a/src/common/config.py b/src/common/config.py
index fd575affa93eefee29e4201aa916a45713486622..960b395167efaa0b54e5b6610e5ea515731af3c1 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -6,7 +6,8 @@
 ## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
 ## Copyright (C) 2005 Travis Shirk <travis@pobox.com>
 ## 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
 ## it under the terms of the GNU General Public License as published
 ## by the Free Software Foundation; version 2 only.
@@ -82,6 +83,7 @@ class Config:
 		'saveposition': [ opt_bool, True ],
 		'mergeaccounts': [ opt_bool, False, '', True ],
 		'sort_by_show': [ opt_bool, True, '', True ],
+		'enable_zeroconf': [opt_bool, False, _('Enable link-local/zeroconf messaging')],
 		'use_speller': [ opt_bool, False, ],
 		'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.')],
@@ -259,6 +261,11 @@ class Config:
 			'msgwin-y-position': [opt_int, -1], # Default is to let the wm decide
 			'msgwin-width': [opt_int, 480],
 			'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': ({
 			'message': [ opt_str, '' ],
diff --git a/src/common/connection.py b/src/common/connection.py
index 095cf2d4fc846d238ce1c74822695245de1e2f48..27a7521ead55b2682710c851fa97d6f2f999f361 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -46,6 +46,7 @@ class Connection(ConnectionHandlers):
 		self.connection = None # xmpppy ClientCommon instance
 		# this property is used to prevent double connections
 		self.last_connection = None # last ClientCommon instance
+		self.is_zeroconf = False
 		self.gpg = None
 		self.status = ''
 		self.priority = gajim.get_priority(name, 'offline')
diff --git a/src/common/gajim.py b/src/common/gajim.py
index 2fcc3d0a554f67da394b1abc9f1530ca6395a228..e353f284cf634bdca8656a12018bb28f7d16add4 100644
--- a/src/common/gajim.py
+++ b/src/common/gajim.py
@@ -120,6 +120,12 @@ status_before_autoaway = {}
 SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
 	'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):
 	pos = jid.find('@')
 	return jid[:pos]
diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py
index ca13af184327e9ff6c0091dfbb6d6bd7f0ca0674..3079ca6f286a9d773aedcd6e93845a874cc7b021 100644
--- a/src/common/xmpp/dispatcher_nb.py
+++ b/src/common/xmpp/dispatcher_nb.py
@@ -74,7 +74,6 @@ class Dispatcher(PlugIn):
 		self.RegisterProtocol('presence', Presence)
 		self.RegisterProtocol('message', Message)
 		self.RegisterDefaultHandler(self.returnStanzaHandler)
-		# Register Gajim's event handler as soon as dispatcher begins
 		self.RegisterEventHandler(self._owner._caller._event_dispatcher)
 		self.on_responses = {}
 		
@@ -84,7 +83,10 @@ class Dispatcher(PlugIn):
 		self._owner.lastErrNode = None
 		self._owner.lastErr = None
 		self._owner.lastErrCode = None
-		self.StreamInit()
+		if hasattr(self._owner, 'StreamInit'):
+			self._owner.StreamInit()
+		else:
+			self.StreamInit()
 	
 	def plugout(self):
 		''' Prepares instance to be destructed. '''
@@ -134,7 +136,7 @@ class Dispatcher(PlugIn):
 				return 0
 		except ExpatError:
 			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()
 			return 0
 		if len(self._pendingExceptions) > 0:
diff --git a/src/common/xmpp/session.py b/src/common/xmpp/session.py
index 3921937ed95f5b05dd60f3e01c88eb4f53e86dd4..b61e4f6deeb7ed7db18d7c3cab18fa919c4e6e25 100644
--- a/src/common/xmpp/session.py
+++ b/src/common/xmpp/session.py
@@ -183,7 +183,7 @@ class Session:
         if self.sendbuffer:
             try:
                 # LOCK_QUEUE
-                sent=self._send(self.sendbuffer)    # âÌÏËÉÒÕÀÝÁÑ ÛÔÕÞËÁ!
+                sent=self._send(self.sendbuffer)    # blocking socket
             except:
                 # UNLOCK_QUEUE
                 self.set_socket_state(SOCKET_DEAD)
diff --git a/src/common/zeroconf/__init__.py b/src/common/zeroconf/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/common/zeroconf/client_zeroconf.py b/src/common/zeroconf/client_zeroconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..6f53b707185d568b92b6ec7c5b7f8eed38b9a94a
--- /dev/null
+++ b/src/common/zeroconf/client_zeroconf.py
@@ -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)
+
diff --git a/src/common/zeroconf/connection_handlers_zeroconf.py b/src/common/zeroconf/connection_handlers_zeroconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..a698107f697b29fff7cea864c67184b8e40325ec
--- /dev/null
+++ b/src/common/zeroconf/connection_handlers_zeroconf.py
@@ -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
+			
diff --git a/src/common/zeroconf/connection_zeroconf.py b/src/common/zeroconf/connection_zeroconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..c75ea4331ed1b12084e239efe7a952c1fa12dd15
--- /dev/null
+++ b/src/common/zeroconf/connection_zeroconf.py
@@ -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
diff --git a/src/common/zeroconf/roster_zeroconf.py b/src/common/zeroconf/roster_zeroconf.py
new file mode 100644
index 0000000000000000000000000000000000000000..7924e70f71152d51fdcbe57b4429f96ae03907fd
--- /dev/null
+++ b/src/common/zeroconf/roster_zeroconf.py
@@ -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
diff --git a/src/common/zeroconf/zeroconf.py b/src/common/zeroconf/zeroconf.py
new file mode 100755
index 0000000000000000000000000000000000000000..b18fdb28ca869bef3432f88223f5feee20e68b3e
--- /dev/null
+++ b/src/common/zeroconf/zeroconf.py
@@ -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)
+'''
diff --git a/src/config.py b/src/config.py
index 1c8cd49fda4edd47c1de772fe7be5f1f19576889..e3aa82d61c5dd2b4dcd65f45172549fb093505c0 100644
--- a/src/config.py
+++ b/src/config.py
@@ -4,6 +4,7 @@
 ## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
 ## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
 ## 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
 ## 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 connection
 from common import passwords
+from common import zeroconf
 from common import dbus_support
 
 from common.exceptions import GajimGeneralException
@@ -1374,6 +1376,9 @@ class AccountModificationWindow:
 		config['custom_host'] = self.xml.get_widget(
 			'custom_host_entry').get_text().decode('utf-8')
 
+		# update in case the name was changed to local account's name
+		config['is_zeroconf'] = False
+
 		config['keyname'] = self.xml.get_widget('gpg_name_label').get_text().decode('utf-8')
 		if config['keyname'] == '': #no key selected
 			config['keyid'] = ''
@@ -1808,6 +1813,22 @@ class AccountsWindow:
 		st = gajim.config.get('mergeaccounts')
 		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):
 		if event.keyval == gtk.keysyms.Escape:
 			self.window.destroy()
@@ -1847,6 +1868,17 @@ class AccountsWindow:
 			dialogs.ErrorDialog(_('Unread events'),
 				_('Read all pending events before removing this account.'))
 			return
+
+		if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+			w = self.xml.get_widget('enable_zeroconf_checkbutton')
+			w.set_active(False)
+		else:
+			if gajim.interface.instances[account].has_key('remove_account'):
+				gajim.interface.instances[account]['remove_account'].window.present()
+			else:
+				gajim.interface.instances[account]['remove_account'] = \
+					RemoveAccountWindow(account)
+
 		win_opened = False
 		if gajim.interface.msg_win_mgr.get_controls(acct = account):
 			win_opened = True
@@ -1891,21 +1923,107 @@ class AccountsWindow:
 		self.show_modification_window(account)
 
 	def show_modification_window(self, account):
-		if gajim.interface.instances[account].has_key('account_modification'):
-			gajim.interface.instances[account]['account_modification'].window.present()
+		if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+			if gajim.interface.instances.has_key('zeroconf_properties'):
+				gajim.interface.instances['zeroconf_properties'].window.present()
+			else:
+				gajim.interface.instances['zeroconf_properties'] = \
+					ZeroconfPropertiesWindow()
 		else:
-			gajim.interface.instances[account]['account_modification'] = \
-				AccountModificationWindow(account)
+			if gajim.interface.instances[account].has_key('account_modification'):
+				gajim.interface.instances[account]['account_modification'].window.present()
+			else:
+				gajim.interface.instances[account]['account_modification'] = \
+					AccountModificationWindow(account)
 
-	def on_merge_checkbutton_toggled(self, widget):
-		gajim.config.set('mergeaccounts', widget.get_active())
+	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):
+		self.on_checkbutton_toggled(widget, 'mergeaccounts')
 		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()
 
+	
+	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:
 	def __init__(self, account, config):
 		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.actions_menu_needs_rebuild = True
 		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 = ''
diff --git a/src/gajim.py b/src/gajim.py
index 5584b89caa12efa97070e2d99c96738510d5508b..413f5705c32fa4381c0829152bf45f6e8b9fb45a 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -31,6 +31,7 @@ import message_control
 from chat_control import ChatControlBase
 
 from common import exceptions
+from common.zeroconf import connection_zeroconf
 
 if os.name == 'posix': # dl module is Unix Only
 	try: # rename the process name to gajim
@@ -618,7 +619,9 @@ class Interface:
 		jids = full_jid_with_resource.split('/', 1)
 		jid = jids[0]
 		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
 				nick = jids[1]
 				if not self.msg_win_mgr.get_control(full_jid_with_resource,
@@ -1406,6 +1409,20 @@ class Interface:
 			if win.startswith('privacy_list_'):
 				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):	
 		'''Check idle status and change that status if needed'''
 		if not self.sleeper.poll():
@@ -1711,6 +1728,7 @@ class Interface:
 			'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
 			'PRIVACY_LISTS_ACTIVE_DEFAULT': \
 				self.handle_event_privacy_lists_active_default,
+			'ZC_NAME_CONFLICT': self.handle_event_zc_name_conflict,
 		}
 		gajim.handlers = self.handlers
 
@@ -1867,9 +1885,13 @@ class Interface:
 			self.handle_event_file_progress)
 		gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
 		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'):
-			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.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
 		gtk.about_dialog_set_url_hook(self.on_launch_browser_mailer, 'url')
diff --git a/src/roster_window.py b/src/roster_window.py
index 881ce56aa237b6424137abcf1bfc3ecc1cf81f8a..1edabc8786646bdbb2ea89828d07b694072e0907 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -1119,6 +1119,14 @@ class RosterWindow:
 		else:
 			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):
 		pointer = self.tree.get_pointer()
 		props = self.tree.get_path_at_pos(pointer[0], pointer[1])
@@ -1363,6 +1371,118 @@ class RosterWindow:
 		if not contact:
 			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')
 		roster_contact_context_menu = xml.get_widget(
 			'roster_contact_context_menu')
@@ -1773,6 +1893,14 @@ class RosterWindow:
 			gajim.interface.instances[account]['account_modification'] = \
 				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):
 		url = 'http://mail.google.com/mail?account_id=%s' % urllib.quote(
 			gajim.config.get_per('accounts', account, 'name'))
@@ -1792,71 +1920,123 @@ class RosterWindow:
 		path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
 		state_images = self.load_iconset(path)
 
-		xml = gtkgui_helpers.get_glade('account_context_menu.glade')
-		account_context_menu = xml.get_widget('account_context_menu')
+		if not gajim.config.get_per('accounts', account, 'is_zeroconf'):
+			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')
+			join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
+			open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem')
+			new_message_menuitem = xml.get_widget('new_message_menuitem')
+			add_contact_menuitem = xml.get_widget('add_contact_menuitem')
+			service_discovery_menuitem = xml.get_widget('service_discovery_menuitem')
+			edit_account_menuitem = xml.get_widget('edit_account_menuitem')
+			sub_menu = gtk.Menu()
+			status_menuitem.set_submenu(sub_menu)
 
-		status_menuitem = xml.get_widget('status_menuitem')
-		join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
-		open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem')
-		new_message_menuitem = xml.get_widget('new_message_menuitem')
-		add_contact_menuitem = xml.get_widget('add_contact_menuitem')
-		service_discovery_menuitem = xml.get_widget('service_discovery_menuitem')
-		edit_account_menuitem = xml.get_widget('edit_account_menuitem')
-		sub_menu = gtk.Menu()
-		status_menuitem.set_submenu(sub_menu)
+			for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
+				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)
 
-		for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
-			uf_show = helpers.get_uf_show(show, use_mnemonic = True)
+			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[show]
+			icon = state_images['offline']
 			item.set_image(icon)
 			sub_menu.append(item)
-			item.connect('activate', self.change_status, account, show)
-
-		item = gtk.SeparatorMenuItem()
-		sub_menu.append(item)
+			item.connect('activate', self.change_status, account, 'offline')
 
-		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)
+			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:
+				open_gmail_inbox_menuitem.connect('activate', self.on_open_gmail_inbox,
+					account)
 
-		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')
+			edit_account_menuitem.connect('activate', self.on_edit_account, account)
+			add_contact_menuitem.connect('activate', self.on_add_new_contact, account)
+			service_discovery_menuitem.connect('activate',
+				self.on_service_disco_menuitem_activate, 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)
 
-		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()
+			# make some items insensitive if account is offline
+			if gajim.connections[account].connected < 2:
+				for widget in [add_contact_menuitem, service_discovery_menuitem,
+				join_group_chat_menuitem, new_message_menuitem]:
+					widget.set_sensitive(False)
 		else:
-			open_gmail_inbox_menuitem.connect('activate', self.on_open_gmail_inbox,
-				account)
+			xml = gtkgui_helpers.get_glade('zeroconf_context_menu.glade')
+			account_context_menu = xml.get_widget('zeroconf_context_menu')
 
-		edit_account_menuitem.connect('activate', self.on_edit_account, account)
-		add_contact_menuitem.connect('activate', self.on_add_new_contact, account)
-		service_discovery_menuitem.connect('activate',
-			self.on_service_disco_menuitem_activate, 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)
+			status_menuitem = xml.get_widget('status_menuitem')
+			#join_group_chat_menuitem =xml.get_widget('join_group_chat_menuitem')
+			new_message_menuitem = xml.get_widget('new_message_menuitem')
+			zeroconf_properties_menuitem = xml.get_widget('zeroconf_properties_menuitem')
+			sub_menu = gtk.Menu()
+			status_menuitem.set_submenu(sub_menu)
 
-		# make some items insensitive if account is offline
-		if gajim.connections[account].connected < 2:
-			for widget in [add_contact_menuitem, service_discovery_menuitem,
-			join_group_chat_menuitem, new_message_menuitem]:
-				widget.set_sensitive(False)
-		
+			for show in ('online', 'away', 'dnd', 'invisible'):
+				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)
+			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
 
 	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):
 				return
 			type = model[list_of_paths[0]][C_TYPE]
+			account = model[list_of_paths[0]][C_ACCOUNT]
 			list_ = []
 			for path in list_of_paths:
 				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,
 					jid)
 				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
 			if type == 'contact':
 				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)
 			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:
 			# dropped before a group : we drop it in the previous group
 			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
 		if type_dest == 'account' and account_source == account_dest:
 			return
+		if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
+			return
 		it = iter_source
 		while model[it][C_TYPE] == 'contact':
 			it = model.iter_parent(it)
diff --git a/src/tooltips.py b/src/tooltips.py
index 7787c26736fef25f129c36686840603300bb9388..be393c6238e3fd089ea09fdf3b4b3d95d50fe69d 100644
--- a/src/tooltips.py
+++ b/src/tooltips.py
@@ -307,7 +307,14 @@ class GCTooltip(BaseTooltip):
 			properties.append((show, None))
 
 		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() != '':
 			properties.append((_('Resource: '), 
 				gtkgui_helpers.escape_for_pango_markup(contact.resource) ))
@@ -408,10 +415,25 @@ class RosterTooltip(NotificationAreaTooltip):
 		vcard_table.set_homogeneous(False)
 		vcard_current_row = 1
 		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
 		# put contacts in dict, where key is priority
 		contacts_dict = {}
@@ -422,6 +444,11 @@ class RosterTooltip(NotificationAreaTooltip):
 					contacts_dict[contact.priority].append(contact)
 				else:
 					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:
 			properties.append((_('Status: '),	' '))
 			transport = gajim.get_transport_name_from_jid(
diff --git a/src/vcard.py b/src/vcard.py
index c1d86afb139d8389fa6449b0a649501c81dd03c9..52b686493713b8895c64eb229c1b6f15bf820a7a 100644
--- a/src/vcard.py
+++ b/src/vcard.py
@@ -2,6 +2,7 @@
 ##
 ## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
 ## 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
 ## 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):
 		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()