gnome-user-share r187 - in trunk: . po



Author: hadess
Date: Tue Jan 22 12:54:01 2008
New Revision: 187
URL: http://svn.gnome.org/viewvc/gnome-user-share?rev=187&view=rev

Log:
2008-01-22  Bastien Nocera  <hadess hadess net>

	* Makefile.am: upd
	* configure.in: Require dbus-glib

	* desktop_gnome_file_sharing.schemas.in:
	* file-share-properties.c:
	Add UI for the Bluetooth file sharing

	* http.c:
	* http.h: Split from user_share.c

	* obexftp.c:
	* obexftp.h: New files with ObexFTP support

	* user_share.c: Remove HTTP code and add ObexFTP support
	* user_share.h: new file

	Add ObexFTP server support to gnome-user-share, requires
	obex-data-server (Closes: #509938)

2008-01-22  Bastien Nocera  <hadess hadess net>

	* POTFILES.in: update for new files



Added:
   trunk/http.c
   trunk/http.h
   trunk/obexftp.c
   trunk/obexftp.h
   trunk/user_share.h
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.in
   trunk/desktop_gnome_file_sharing.schemas.in
   trunk/file-share-properties.c
   trunk/file-share-properties.glade
   trunk/po/ChangeLog
   trunk/po/POTFILES.in
   trunk/user_share.c

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Tue Jan 22 12:54:01 2008
@@ -43,8 +43,13 @@
 	$(USER_SHARE_CONFIG_CFLAGS)			\
 	$(X_CFLAGS)
 
-gnome_user_share_SOURCES = \
-	user_share.c
+gnome_user_share_SOURCES =	\
+	user_share.c		\
+	user_share.h		\
+	http.c			\
+	http.h			\
+	obexftp.c		\
+	obexftp.h
 
 gnome_user_share_LDADD = \
 	$(HOWL_LIBS)	\

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Tue Jan 22 12:54:01 2008
@@ -69,7 +69,7 @@
   AC_MSG_ERROR([Neither avahi nor howl detected. Gnome-user-share needs a mDNS implementation.])
 fi
 
-PKG_CHECK_MODULES(USER_SHARE, glib-2.0 >= 2.15.2 gconf-2.0 $DBUS_MODULES)
+PKG_CHECK_MODULES(USER_SHARE, glib-2.0 >= 2.15.2 gconf-2.0 dbus-glib-1 $DBUS_MODULES)
 AC_SUBST(USER_SHARE_CFLAGS)
 AC_SUBST(USER_SHARE_LIBS)
 

Modified: trunk/desktop_gnome_file_sharing.schemas.in
==============================================================================
--- trunk/desktop_gnome_file_sharing.schemas.in	(original)
+++ trunk/desktop_gnome_file_sharing.schemas.in	Tue Jan 22 12:54:01 2008
@@ -8,8 +8,8 @@
       <type>bool</type>
       <default>false</default>
       <locale name="C">
-        <short>Share Public directory</short>
-        <long>If this is true, the Public directory in the users home directory will be shared when the user is logged in.</long>
+        <short>Share Public directory over the network</short>
+        <long>If this is true, the Public directory in the users home directory will be shared over the network when the user is logged in.</long>
       </locale>
     </schema>
     <schema>
@@ -23,5 +23,27 @@
 	<long>When to ask for passwords. Possible values are "never", "on_write", and "always".</long>
       </locale>
     </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/file_sharing/bluetooth_enabled</key>
+      <applyto>/desktop/gnome/file_sharing/bluetooth_enabled</applyto>
+      <owner>gnome-user-share</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Share Public directory over Bluetooth</short>
+        <long>If this is true, the Public directory in the users home directory will be shared over Bluetooth when the user is logged in.</long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/desktop/gnome/file_sharing/bluetooth_allow_write</key>
+      <applyto>/desktop/gnome/file_sharing/bluetooth_allow_write</applyto>
+      <owner>gnome-user-share</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Whether to allow Bluetooth clients to write files.</short>
+        <long>Whether to allow Bluetooth clients to write files, or share the files read-only.</long>
+      </locale>
+    </schema>
   </schemalist>
 </gconfschemafile>

Modified: trunk/file-share-properties.c
==============================================================================
--- trunk/file-share-properties.c	(original)
+++ trunk/file-share-properties.c	Tue Jan 22 12:54:01 2008
@@ -33,7 +33,9 @@
 
 #define FILE_SHARING_DIR "/desktop/gnome/file_sharing"
 #define FILE_SHARING_ENABLED "/desktop/gnome/file_sharing/enabled"
+#define FILE_SHARING_BLUETOOTH_ENABLED "/desktop/gnome/file_sharing/bluetooth_enabled"
 #define FILE_SHARING_REQUIRE_PASSWORD "/desktop/gnome/file_sharing/require_password"
+#define FILE_SHARING_BLUETOOTH_ALLOW_WRITE "/desktop/gnome/file_sharing/bluetooth_allow_write"
 
 #define REALM "Please log in as the user guest"
 #define USER "guest"
@@ -130,34 +132,50 @@
 update_ui (void)
 {
     GConfClient *client;
-    gboolean enabled;
+    gboolean enabled, bluetooth_enabled, bluetooth_write_enabled;
     char *str;
     PasswordSetting password_setting;
     GtkWidget *check;
     GtkWidget *password_combo;
     GtkWidget *password_entry;
+    GtkWidget *bluetooth_check;
+    GtkWidget *allow_write_bluetooth_check;
 
     client = gconf_client_get_default ();
 
     enabled = gconf_client_get_bool (client,
 				     FILE_SHARING_ENABLED,
 				     NULL);
+    bluetooth_enabled = gconf_client_get_bool (client,
+    					       FILE_SHARING_BLUETOOTH_ENABLED,
+    					       NULL);
+    bluetooth_write_enabled = gconf_client_get_bool (client,
+						     FILE_SHARING_BLUETOOTH_ALLOW_WRITE,
+						     NULL);
+
     str = gconf_client_get_string (client, FILE_SHARING_REQUIRE_PASSWORD, NULL);
     password_setting = password_setting_from_string (str);
     g_free (str);
-    
+
     check = glade_xml_get_widget (ui, "enable_check");
     password_combo = glade_xml_get_widget (ui, "password_combo");
     password_entry = glade_xml_get_widget (ui, "password_entry");
-    
-    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
-				  enabled);
+    bluetooth_check = glade_xml_get_widget (ui, "enable_bluetooth_check");
+    allow_write_bluetooth_check = glade_xml_get_widget (ui, "allow_write_bluetooth_check");
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), enabled);
     gtk_widget_set_sensitive (password_combo, enabled);
     gtk_widget_set_sensitive (password_entry, enabled && password_setting != PASSWORD_NEVER);
 
     gtk_combo_box_set_active (GTK_COMBO_BOX (password_combo),
 			      password_setting);
-    
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bluetooth_check), bluetooth_enabled);
+    gtk_widget_set_sensitive (allow_write_bluetooth_check, bluetooth_enabled);
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (allow_write_bluetooth_check),
+    				  bluetooth_write_enabled);
+
     g_object_unref (client);
 }
 
@@ -179,6 +197,23 @@
     update_ui ();
 }
 
+static void
+file_sharing_bluetooth_enabled_changed (GConfClient* client,
+					guint cnxn_id,
+					GConfEntry *entry,
+					gpointer data)
+{
+	update_ui ();
+}
+
+static void
+file_sharing_bluetooth_allow_write_changed (GConfClient* client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    gpointer data)
+{
+	update_ui ();
+}
 
 static void
 password_combo_changed (GtkComboBox *combo_box)
@@ -198,25 +233,11 @@
 }
 
 static void
-enable_check_toggled (GtkWidget *check)
+launch_share (void)
 {
-    GConfClient *client;
-    gboolean enabled;
-    char *argv[2];
-    int i;
-
-    enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
-    
-    client = gconf_client_get_default ();
+	char *argv[2];
+	int i;
 
-    gconf_client_set_bool (client,
-			   FILE_SHARING_ENABLED,
-			   enabled,
-			   NULL);
-    
-    g_object_unref (client);
-
-    if (enabled) {
 	i = 0;
 	argv[i++] = USER_SHARE_PROGRAM;
 	argv[i++] = NULL;
@@ -229,19 +250,77 @@
 			    NULL,
 			    NULL,
 			    NULL)) {
-	    g_warning ("Unable to start gnome-user-share program");
+		g_warning ("Unable to start gnome-user-share program");
 	}
-    }
+}
+
+static void
+enable_bluetooth_check_toggled (GtkWidget *check)
+{
+	GConfClient *client;
+	gboolean enabled;
+
+	enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_set_bool (client,
+			       FILE_SHARING_BLUETOOTH_ENABLED,
+			       enabled,
+			       NULL);
+
+	g_object_unref (client);
+
+	if (enabled != FALSE)
+		launch_share ();
+}
+
+static void
+enable_check_toggled (GtkWidget *check)
+{
+	GConfClient *client;
+	gboolean enabled;
+
+	enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_set_bool (client,
+			       FILE_SHARING_ENABLED,
+			       enabled,
+			       NULL);
+
+	g_object_unref (client);
+
+	if (enabled != FALSE)
+		launch_share ();
 }
 
 static void
 password_entry_changed (GtkEditable *editable)
 {
-    g_object_set_data (G_OBJECT (editable),
-		       "user_edited", GINT_TO_POINTER (1));
-    flush_password ();
+	g_object_set_data (G_OBJECT (editable),
+			   "user_edited", GINT_TO_POINTER (1));
+	flush_password ();
 }
 
+static void
+bluetooth_allow_write_check_toggled (GtkWidget *check)
+{
+	GConfClient *client;
+	gboolean enabled;
+
+	enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
+
+	client = gconf_client_get_default ();
+
+	gconf_client_set_bool (client,
+			       FILE_SHARING_BLUETOOTH_ALLOW_WRITE,
+			       enabled,
+			       NULL);
+
+	g_object_unref (client);
+}
 
 int
 main (int argc, char *argv[])
@@ -250,6 +329,8 @@
     GtkWidget *check;
     GtkWidget *password_combo;
     GtkWidget *password_entry;
+    GtkWidget *bluetooth_check;
+    GtkWidget *bluetooth_allow_write_check;
     GtkWidget *window;
     GtkListStore *store;
     GtkCellRenderer *cell;
@@ -278,6 +359,8 @@
     check = glade_xml_get_widget (ui, "enable_check");
     password_combo = glade_xml_get_widget (ui, "password_combo");
     password_entry = glade_xml_get_widget (ui, "password_entry");
+    bluetooth_check = glade_xml_get_widget (ui, "enable_bluetooth_check");
+    bluetooth_allow_write_check = glade_xml_get_widget (ui, "allow_write_bluetooth_check");
 
     store = gtk_list_store_new (1, G_TYPE_STRING);
     gtk_combo_box_set_model (GTK_COMBO_BOX (password_combo),
@@ -313,6 +396,11 @@
 		      "toggled", G_CALLBACK (enable_check_toggled), NULL);
     g_signal_connect (password_combo,
 		      "changed", G_CALLBACK (password_combo_changed), NULL);
+    g_signal_connect (bluetooth_check,
+    		      "toggled", G_CALLBACK (enable_bluetooth_check_toggled), NULL);
+    g_signal_connect (bluetooth_allow_write_check,
+    		      "toggled", G_CALLBACK (bluetooth_allow_write_check_toggled), NULL);
+
     g_signal_connect (glade_xml_get_widget (ui, "close_button"),
 		      "clicked", G_CALLBACK (gtk_main_quit), NULL);
 
@@ -329,6 +417,18 @@
 			     NULL,
 			     NULL,
 			     NULL);
+    gconf_client_notify_add (client,
+			     FILE_SHARING_BLUETOOTH_ENABLED,
+			     file_sharing_bluetooth_enabled_changed,
+			     NULL,
+			     NULL,
+			     NULL);
+    gconf_client_notify_add (client,
+			     FILE_SHARING_BLUETOOTH_ALLOW_WRITE,
+			     file_sharing_bluetooth_allow_write_changed,
+			     NULL,
+			     NULL,
+			     NULL);
 
     g_object_unref (client);
 

Modified: trunk/file-share-properties.glade
==============================================================================
--- trunk/file-share-properties.glade	(original)
+++ trunk/file-share-properties.glade	Tue Jan 22 12:54:01 2008
@@ -5,7 +5,6 @@
 
 <widget class="GtkDialog" id="user_share_dialog">
   <property name="border_width">12</property>
-  <property name="visible">False</property>
   <property name="title" translatable="yes">File Sharing Preferences</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
   <property name="window_position">GTK_WIN_POS_NONE</property>
@@ -17,6 +16,8 @@
   <property name="skip_pager_hint">False</property>
   <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
   <property name="has_separator">False</property>
 
   <child internal-child="vbox">
@@ -73,7 +74,7 @@
 	      <child>
 		<widget class="GtkTable" id="table2">
 		  <property name="visible">True</property>
-		  <property name="n_rows">3</property>
+		  <property name="n_rows">5</property>
 		  <property name="n_columns">2</property>
 		  <property name="homogeneous">False</property>
 		  <property name="row_spacing">6</property>
@@ -93,6 +94,10 @@
 		      <property name="xpad">0</property>
 		      <property name="ypad">0</property>
 		      <property name="mnemonic_widget">password_combo</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>
@@ -107,6 +112,8 @@
 		  <child>
 		    <widget class="GtkComboBox" id="password_combo">
 		      <property name="visible">True</property>
+		      <property name="add_tearoffs">False</property>
+		      <property name="focus_on_click">True</property>
 		    </widget>
 		    <packing>
 		      <property name="left_attach">1</property>
@@ -131,6 +138,10 @@
 		      <property name="xpad">0</property>
 		      <property name="ypad">0</property>
 		      <property name="mnemonic_widget">password_entry</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
 		    </widget>
 		    <packing>
 		      <property name="left_attach">0</property>
@@ -151,6 +162,7 @@
 		      <property name="max_length">0</property>
 		      <property name="text"></property>
 		      <property name="has_frame">True</property>
+		      <property name="invisible_char">â</property>
 		      <property name="activates_default">False</property>
 		    </widget>
 		    <packing>
@@ -183,6 +195,50 @@
 		      <property name="y_options"></property>
 		    </packing>
 		  </child>
+
+		  <child>
+		    <widget class="GtkCheckButton" id="enable_bluetooth_check">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Share Public files over Bluetooth</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="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>
+
+		  <child>
+		    <widget class="GtkCheckButton" id="allow_write_bluetooth_check">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Allow remote devices to write to modify files</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="left_attach">0</property>
+		      <property name="right_attach">2</property>
+		      <property name="top_attach">4</property>
+		      <property name="bottom_attach">5</property>
+		      <property name="x_options">fill</property>
+		      <property name="y_options"></property>
+		    </packing>
+		  </child>
 		</widget>
 	      </child>
 	    </widget>
@@ -201,6 +257,10 @@
 	      <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">label_item</property>

Added: trunk/http.c
==============================================================================
--- (empty file)
+++ trunk/http.c	Tue Jan 22 12:54:01 2008
@@ -0,0 +1,625 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
+ *
+ *  Nautilus is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Nautilus is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors: Alexander Larsson <alexl redhat com>
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <X11/Xlib.h>
+
+#ifdef HAVE_DBUS_1_1
+#include <dbus/dbus.h>
+#endif
+
+#ifdef HAVE_AVAHI
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-glib/glib-watch.h>
+#include <avahi-glib/glib-malloc.h>
+#endif /* HAVE_AVAHI */
+
+#ifdef HAVE_HOWL
+/* Workaround broken howl installing config.h */
+#undef PACKAGE
+#undef VERSION
+#include <howl.h>
+#endif /* HAVE_HOWL */
+
+#include <gconf/gconf-client.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include "user_share.h"
+#include "http.h"
+
+#ifdef HAVE_DBUS_1_1
+static char *dbus_session_id;
+#endif
+
+static pid_t httpd_pid = 0;
+
+static int
+get_port (void)
+{
+	int sock;
+	struct sockaddr_in addr;
+	int reuse;
+	socklen_t len;
+
+	sock = socket (PF_INET, SOCK_STREAM, 0);
+	if (sock < 0) {
+		return -1;
+	}
+
+	memset (&addr, 0, sizeof (addr));
+	addr.sin_port = 0;
+	addr.sin_addr.s_addr = INADDR_ANY;
+
+	reuse = 1;
+	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
+	if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == -1) {
+		close (sock);
+		return -1;
+	}
+
+	len = sizeof (addr);
+	if (getsockname (sock, (struct sockaddr *)&addr, &len) == -1) {
+		close (sock);
+		return -1;
+	}
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+	/* XXX This exposes a potential race condition, but without this,
+	 * httpd will not start on the above listed platforms due to the fact
+	 * that SO_REUSEADDR is also needed when Apache binds to the listening
+	 * socket.  At this time, Apache does not support that socket option.
+	 */
+	close (sock);
+#endif
+	return ntohs (addr.sin_port);
+}
+
+
+static char *
+get_share_name (void)
+{
+	static char *name = NULL;
+	const char *host_name;
+
+	if (name == NULL) {
+		host_name = g_get_host_name ();
+		if (strcmp (host_name, "localhost") == 0) {
+			/* Translators: The %s will get filled in with the user name
+			   of the user, to form a genitive. If this is difficult to
+			   translate correctly so that it will work correctly in your
+			   language, you may use something equivalent to
+			   "Public files of %s", or leave out the %s altogether.
+			   In the latter case, please put "%.0s" somewhere in the string,
+			   which will match the user name string passed by the C code,
+			   but not put the user name in the final string. This is to
+			   avoid the warning that msgfmt might otherwise generate. */
+			name = g_strdup_printf (_("%s's public files"), g_get_user_name ());
+		} else {
+			/* Translators: This is similar to the string before, only it
+			   has the hostname in it too. */
+			name = g_strdup_printf (_("%s's public files on %s"),
+						g_get_user_name (),
+						host_name);
+		}
+
+	}
+	return name;
+}
+
+#ifdef HAVE_DBUS_1_1
+static void
+init_dbus() {
+	/* The only use we make of D-BUS is to fetch the session BUS ID so we can export
+	 * it via mDNS, so we connect and then immediately disconnect. If we were using
+	 * the D-BUS session BUS for something persistent, the following code should use
+	 * dbus_bus_get() and skip the shutdown. (Avahi uses the D-BUS  _system_ bus
+	 * internally.)
+	 */
+
+	DBusError derror;
+	DBusConnection *connection;
+
+	dbus_error_init(&derror);
+
+	connection = dbus_bus_get_private(DBUS_BUS_SESSION, &derror);
+	if (connection == NULL) {
+		g_printerr("Failed to connect to session bus: %s", derror.message);
+		dbus_error_free(&derror);
+		return;
+	}
+
+	dbus_session_id = dbus_bus_get_id(connection, &derror);
+	if (dbus_session_id == NULL) {
+		/* This can happen if the D-BUS library has been upgraded to 1.1, but the
+		 * user's session hasn't yet been restarted
+		 */
+		g_printerr("Failed to get session BUS ID: %s", derror.message);
+		dbus_error_free(&derror);
+	}
+
+	dbus_connection_set_exit_on_disconnect(connection, FALSE);
+	dbus_connection_close(connection);
+	dbus_connection_unref(connection);
+}
+#endif
+
+#ifdef HAVE_AVAHI
+
+static AvahiClient *avahi_client = NULL;
+static gboolean avahi_should_publish = FALSE;
+static gboolean avahi_running = FALSE;
+static int avahi_port = 0;
+static AvahiEntryGroup *entry_group = NULL;
+static char *avahi_name = NULL;
+
+static AvahiStringList*
+new_text_record_list (const char *first_key,
+		      const char *first_value,
+		      ...)
+{
+	va_list args;
+	const char *k;
+	const char *v;
+	AvahiStringList *list;
+
+	if (first_key == NULL)
+		return NULL;
+
+	list = NULL;
+
+	list = avahi_string_list_add_pair (list, first_key, first_value);
+
+	va_start (args, first_value);
+	k = va_arg (args, const char*);
+	if (k)
+		v = va_arg (args, const char*);
+	while (k != NULL) {
+		list = avahi_string_list_add_pair (list, k, v);
+
+		k = va_arg (args, const char*);
+		if (k)
+			v = va_arg (args, const char*);
+	}
+
+	va_end(args);
+
+	return list;
+}
+
+static gboolean
+create_service (void) {
+	int ret;
+	AvahiStringList *txt_records;
+
+	if (avahi_name == NULL) {
+		avahi_name = g_strdup (get_share_name ());
+	}
+
+	txt_records = new_text_record_list ("u", "guest",
+#ifdef HAVE_DBUS_1_1
+					    /* This must be last */
+					    dbus_session_id != NULL ? "org.freedesktop.od.session" : NULL, dbus_session_id,
+#endif									   
+					    NULL);
+
+	ret = avahi_entry_group_add_service_strlst (entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 
+						    AVAHI_PUBLISH_USE_MULTICAST,
+						    avahi_name, "_webdav._tcp", NULL, NULL, 
+						    avahi_port,
+						    txt_records);
+
+	avahi_string_list_free(txt_records);
+
+	if (ret < 0) {
+		return FALSE;
+	}
+
+	ret = avahi_entry_group_commit (entry_group);
+
+	if (ret < 0) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void 
+entry_group_callback (AvahiEntryGroup *g, 
+		      AvahiEntryGroupState state, 
+		      void *userdata) 
+{
+	if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+	} else if (state == AVAHI_ENTRY_GROUP_COLLISION) {
+		char *n;
+
+		/* A service name collision happened. Let's pick a new name */
+		n = avahi_alternative_service_name (avahi_name);
+		g_free (avahi_name);
+		avahi_name = n;
+
+		fprintf (stderr, "Service name collision, renaming service to '%s'\n", avahi_name);
+
+		/* And recreate the services */
+		create_service();
+	}
+}
+
+static void
+avahi_client_callback (AvahiClient *client, AvahiClientState state, void *userdata)
+{
+	if (state == AVAHI_CLIENT_S_RUNNING) {
+		avahi_running = TRUE;
+		if (avahi_should_publish) {
+			create_service ();
+		}
+	} else if (state == AVAHI_CLIENT_S_COLLISION) {
+		avahi_entry_group_reset (entry_group);
+	} else if (state == AVAHI_CLIENT_FAILURE) {
+		avahi_running = FALSE;
+	}
+}
+
+static gboolean
+init_avahi (void)
+{
+	AvahiGLibPoll *poll;
+	int error;
+
+	avahi_set_allocator (avahi_glib_allocator ());
+
+	poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT);
+
+	/* Create a new AvahiClient instance */
+	avahi_client = avahi_client_new (avahi_glib_poll_get (poll),
+					 AVAHI_CLIENT_NO_FAIL,
+					 avahi_client_callback,
+					 NULL,
+					 &error);
+	if (avahi_client == NULL) {
+		return FALSE;
+	}
+
+	entry_group = avahi_entry_group_new (avahi_client, entry_group_callback, NULL);
+	if (entry_group == NULL) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+static gboolean
+publish_service (int port)
+{
+	avahi_should_publish = TRUE;
+	avahi_port = port;
+	if (avahi_running) {
+		create_service ();
+	}
+	return TRUE;
+}
+
+static void
+stop_publishing (void)
+{
+	avahi_should_publish = FALSE;
+	avahi_entry_group_reset (entry_group);
+}
+#endif
+
+#ifdef HAVE_HOWL
+
+static sw_discovery_publish_id published_id = 0;
+static sw_discovery howl_session;
+
+static gboolean
+howl_input (GIOChannel  *io_channel,
+	    GIOCondition cond,
+	    gpointer     callback_data)
+{
+	sw_discovery session;
+	session = callback_data;
+	sw_salt salt;
+
+	if (sw_discovery_salt (session, &salt) == SW_OKAY) {
+		sw_salt_lock (salt);
+		sw_discovery_read_socket (session);
+		sw_salt_unlock (salt);
+	}
+	return TRUE;
+}
+
+static void
+set_up_howl_session (sw_discovery session)
+{
+	int fd;
+	GIOChannel *channel;
+
+	fd = sw_discovery_socket (session);
+
+	channel = g_io_channel_unix_new (fd);
+	g_io_add_watch (channel,
+			G_IO_IN,
+			howl_input, session);
+	g_io_channel_unref (channel);
+}
+
+static sw_result
+publish_reply (sw_discovery			discovery,
+	       sw_discovery_publish_status	status,
+	       sw_discovery_oid			id,
+	       sw_opaque			extra)
+{
+	return SW_OKAY;
+}
+
+
+static gboolean
+publish_service (int port)
+{
+	sw_result result;
+
+	result = sw_discovery_publish (howl_session, 0,
+				       get_share_name (),
+				       "_webdav._tcp",
+				       NULL, NULL,
+				       port,
+				       /* TODO: should be u=guest */
+				       /* text */ (unsigned char *) "", 0,
+				       publish_reply, NULL, &published_id);
+	if (result != SW_OKAY) {
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+stop_publishing (void)
+{
+	if (published_id != 0)
+		sw_discovery_cancel (howl_session, published_id);
+	published_id = 0;
+}
+#endif /* HAVE_HOWL */
+
+
+static void
+ensure_conf_dir (void)
+{
+	char *dirname;
+
+	dirname = g_build_filename (g_get_home_dir (), ".gnome2", "user-share", NULL);
+	g_mkdir_with_parents (dirname, 0755);
+	g_free (dirname);
+}
+
+static void
+httpd_child_setup (gpointer user_data)
+{
+#ifdef HAVE_SELINUX
+	char *mycon;
+	/* If selinux is enabled, avoid transitioning to the httpd_t context,
+	   as this normally means you can't read the users homedir. */
+	if (is_selinux_enabled()) {
+		if (getcon (&mycon) < 0) {
+			abort ();
+		}
+		if (setexeccon (mycon) < 0)
+			abort ();
+		freecon (mycon);
+	}
+#endif
+}
+
+static gboolean
+spawn_httpd (int port, pid_t *pid_out)
+{
+	char *free1, *free2, *free3;
+	gboolean res;
+	char *argv[10];
+	char *env[10];
+	int i;
+	gint status;
+	char *pid_filename;
+	char *pidfile;
+	GError *error;
+	gboolean got_pidfile;
+	GConfClient *client;
+	char *str;
+	char *public_dir;
+
+	public_dir = lookup_public_dir ();
+	ensure_conf_dir ();
+
+	i = 0;
+	argv[i++] = HTTPD_PROGRAM;
+	argv[i++] = "-f";
+	argv[i++] = HTTPD_CONFIG;
+	argv[i++] = "-C";
+	free1 = argv[i++] = g_strdup_printf ("Listen %d", port);
+
+	client = gconf_client_get_default ();
+	str = gconf_client_get_string (client,
+				       FILE_SHARING_REQUIRE_PASSWORD, NULL);
+
+	if (str && strcmp (str, "never") == 0) {
+		/* Do nothing */
+	} else if (str && strcmp (str, "on_write") == 0){
+		argv[i++] = "-D";
+		argv[i++] = "RequirePasswordOnWrite";
+	} else {
+		/* always, or safe fallback */
+		argv[i++] = "-D";
+		argv[i++] = "RequirePasswordAlways";
+	}
+	g_free (str);
+	g_object_unref (client);
+
+	argv[i] = NULL;
+
+	i = 0;
+	free2 = env[i++] = g_strdup_printf("HOME=%s", g_get_home_dir());
+	free3 = env[i++] = g_strdup_printf("XDG_PUBLICSHARE_DIR=%s", public_dir);
+	env[i++] = "LANG=C";
+	env[i] = NULL;
+
+	pid_filename = g_build_filename (g_get_home_dir (), ".gnome2/user-share/pid", NULL);
+
+	/* Remove pid file before spawning to avoid races with child and old pidfile */
+	unlink (pid_filename);
+
+	error = NULL;
+	res = g_spawn_sync (g_get_home_dir(),
+			    argv, env, 0,
+			    httpd_child_setup, NULL,
+			    NULL, NULL,
+			    &status,
+			    &error);
+	g_free (free1);
+	g_free (free2);
+	g_free (free3);
+	g_free (public_dir);
+
+	if (!res) {
+		fprintf (stderr, "error spawning httpd: %s\n",
+			 error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	if (status != 0) {
+		g_free (pid_filename);
+		return FALSE;
+	}
+
+	got_pidfile = FALSE;
+	error = NULL;
+	for (i = 0; i < 5; i++) {
+		if (error != NULL) 
+			g_error_free (error); 
+		error = NULL;
+		if (g_file_get_contents (pid_filename, &pidfile, NULL, &error)) {
+			got_pidfile = TRUE;
+			*pid_out = atoi (pidfile);
+			g_free (pidfile);
+			break;
+		}
+		sleep (1);
+	}
+
+	g_free (pid_filename);
+
+	if (!got_pidfile) {
+		fprintf (stderr, "error opening httpd pidfile: %s\n", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+kill_httpd (void)
+{
+	if (httpd_pid != 0) {
+		kill (httpd_pid, SIGTERM);
+
+		/* Allow child time to die, we can't waitpid, because its
+		   not a direct child */
+		sleep (1);
+	}
+	httpd_pid = 0;
+}
+
+void
+http_up (void)
+{
+	guint port;
+
+	port = get_port ();
+	if (!spawn_httpd (port, &httpd_pid)) {
+		fprintf (stderr, "spawning httpd failed\n");
+	} else {
+		if (!publish_service (port)) {
+			fprintf (stderr, "publishing failed\n");
+		}
+	}
+}
+
+void
+http_down (void)
+{
+	kill_httpd ();
+	stop_publishing ();
+}
+
+gboolean
+http_init (void)
+{
+#ifdef HAVE_DBUS_1_1
+	init_dbus();
+#endif	
+
+#ifdef HAVE_AVAHI
+	if (!init_avahi ()) {
+		/* Print out the error string */
+		fprintf (stderr, "avahi init failed\n");
+		return FALSE;
+	}
+#endif
+#ifdef HAVE_HOWL
+	if (sw_discovery_init (&howl_session) != SW_OKAY) {
+		fprintf (stderr, "howl init failed\n");
+		return FALSE;
+	}
+	set_up_howl_session (howl_session);
+#endif
+
+	return TRUE;
+}
+
+pid_t
+http_get_pid (void)
+{
+	return httpd_pid;
+}
+

Added: trunk/http.h
==============================================================================
--- (empty file)
+++ trunk/http.h	Tue Jan 22 12:54:01 2008
@@ -0,0 +1,28 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
+ *
+ *  Nautilus is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Nautilus is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors: Alexander Larsson <alexl redhat com>
+ *
+ */
+
+void http_up (void);
+void http_down (void);
+gboolean http_init (void);
+pid_t http_get_pid (void);
+

Added: trunk/obexftp.c
==============================================================================
--- (empty file)
+++ trunk/obexftp.c	Tue Jan 22 12:54:01 2008
@@ -0,0 +1,152 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
+ *
+ *  Nautilus is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Nautilus is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors: Bastien Nocera <hadess hadess net>
+ *
+ */
+
+#include "config.h"
+
+#include <dbus/dbus-glib.h>
+#include <gconf/gconf-client.h>
+
+#include <string.h>
+
+#include "obexftp.h"
+#include "user_share.h"
+
+static DBusGConnection *connection = NULL;
+static DBusGProxy *manager_proxy = NULL;
+static DBusGProxy *server_proxy = NULL;
+
+void
+obexftp_up (void)
+{
+	GError *err = NULL;
+	GConfClient *client;
+	char *public_dir, *session;
+	gboolean allow_write;
+
+	session = NULL;
+	if (manager_proxy == NULL) {
+		manager_proxy = dbus_g_proxy_new_for_name (connection,
+							   "org.openobex",
+							   "/org/openobex",
+							   "org.openobex.Manager");
+		if (dbus_g_proxy_call (manager_proxy, "CreateBluetoothServer",
+				       &err, G_TYPE_STRING, "00:00:00:00:00:00", G_TYPE_STRING, "ftp", G_TYPE_INVALID,
+				       G_TYPE_STRING, &session, G_TYPE_INVALID) == FALSE) {
+			g_printerr ("Creating Bluetooth server failed: %s\n",
+				    err->message);
+			g_error_free (err);
+			g_object_unref (manager_proxy);
+			manager_proxy = NULL;
+			return;
+		}
+	}
+
+	public_dir = lookup_public_dir ();
+	client = gconf_client_get_default ();
+	allow_write = gconf_client_get_bool (client, FILE_SHARING_BLUETOOTH_ALLOW_WRITE, NULL);
+	g_object_unref (client);
+
+	if (server_proxy == NULL) {
+		server_proxy = dbus_g_proxy_new_for_name (connection,
+							   "org.openobex",
+							   session,
+							   "org.openobex.Server");
+		g_free (session);
+	}
+	if (dbus_g_proxy_call (server_proxy, "Start", &err,
+			   G_TYPE_STRING, public_dir, G_TYPE_BOOLEAN, !allow_write, G_TYPE_INVALID,
+			   G_TYPE_INVALID) == FALSE) {
+		g_printerr ("Starting Bluetooth server session failed: %s\n",
+			    err->message);
+		g_error_free (err);
+		g_free (public_dir);
+		g_object_unref (server_proxy);
+		server_proxy = NULL;
+		g_object_unref (manager_proxy);
+		manager_proxy = NULL;
+		return;
+	}
+
+	g_free (public_dir);
+}
+
+static void
+obexftp_stop (gboolean stop_manager)
+{
+	GError *err = NULL;
+
+	if (server_proxy == NULL)
+		return;
+
+	if (dbus_g_proxy_call (server_proxy, "Stop", &err, G_TYPE_INVALID, G_TYPE_INVALID) == FALSE) {
+		const gchar *error_name = NULL;
+
+		if (err != NULL && err->code == DBUS_GERROR_REMOTE_EXCEPTION)
+			error_name = dbus_g_error_get_name (err);
+		if (error_name == NULL ||
+		    (error_name != NULL && strcmp (error_name, "org.openobex.Error.NotStarted") != 0)) {
+			g_printerr ("Stopping Bluetooth server session failed: %s\n",
+				    err->message);
+			g_error_free (err);
+			return;
+		}
+		g_error_free (err);
+	}
+
+	if (stop_manager != FALSE) {
+		g_object_unref (server_proxy);
+		server_proxy = NULL;
+		g_object_unref (manager_proxy);
+		manager_proxy = NULL;
+	}
+}
+
+void
+obexftp_down (void)
+{
+	obexftp_stop (TRUE);
+}
+
+void
+obexftp_restart (void)
+{
+	obexftp_stop (FALSE);
+	obexftp_up ();
+}
+
+gboolean
+obexftp_init (void)
+{
+	GError *err = NULL;
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &err);
+	if (connection == NULL) {
+		g_printerr ("Connecting to session bus failed: %s\n",
+			    err->message);
+		g_error_free (err);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+

Added: trunk/obexftp.h
==============================================================================
--- (empty file)
+++ trunk/obexftp.h	Tue Jan 22 12:54:01 2008
@@ -0,0 +1,30 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
+ *
+ *  Nautilus is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Nautilus is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors: Bastien Nocera <hadess hadess net>
+ *
+ */
+
+#include <glib.h>
+
+void obexftp_up (void);
+void obexftp_down (void);
+void obexftp_restart (void);
+gboolean obexftp_init (void);
+

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Tue Jan 22 12:54:01 2008
@@ -5,4 +5,5 @@
 file-share-properties.glade
 gnome-user-share-properties.desktop.in
 gnome-user-share.desktop.in.in
+http.c
 user_share.c

Modified: trunk/user_share.c
==============================================================================
--- trunk/user_share.c	(original)
+++ trunk/user_share.c	Tue Jan 22 12:54:01 2008
@@ -1,7 +1,7 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
 
 /*
- *  Copyright (C) 2004 Red Hat, Inc.
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
  *
  *  Nautilus is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License as
@@ -27,25 +27,9 @@
 #include <glib/gi18n.h>
 #include <X11/Xlib.h>
 
-#ifdef HAVE_DBUS_1_1
-#include <dbus/dbus.h>
-#endif
-
-#ifdef HAVE_AVAHI
-#include <avahi-client/client.h>
-#include <avahi-client/publish.h>
-#include <avahi-common/alternative.h>
-#include <avahi-common/error.h>
-#include <avahi-glib/glib-watch.h>
-#include <avahi-glib/glib-malloc.h>
-#endif /* HAVE_AVAHI */
-
-#ifdef HAVE_HOWL
-/* Workaround broken howl installing config.h */
-#undef PACKAGE
-#undef VERSION
-#include <howl.h>
-#endif /* HAVE_HOWL */
+#include "user_share.h"
+#include "http.h"
+#include "obexftp.h"
 
 #include <gconf/gconf-client.h>
 
@@ -55,28 +39,11 @@
 #include <stdlib.h>
 #include <signal.h>
 #include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
-#define FILE_SHARING_DIR "/desktop/gnome/file_sharing"
-#define FILE_SHARING_ENABLED "/desktop/gnome/file_sharing/enabled"
-#define FILE_SHARING_REQUIRE_PASSWORD "/desktop/gnome/file_sharing/require_password"
-
-#ifdef HAVE_DBUS_1_1
-static char *dbus_session_id;
-#endif
 
 static GMainLoop *loop = NULL;
-static pid_t httpd_pid = 0;
 static guint disabled_timeout_tag = 0;
 
-static char *
+char *
 lookup_public_dir (void)
 {
 	const char *public_dir;
@@ -93,704 +60,217 @@
 	return dir;
 }
 
-static int
-get_port (void)
-{
-    int sock;
-    struct sockaddr_in addr;
-    int reuse;
-    socklen_t len;
-    
-    sock = socket (PF_INET, SOCK_STREAM, 0);
-    if (sock < 0) {
-		return -1;
-    }
-    
-    memset (&addr, 0, sizeof (addr));
-    addr.sin_port = 0;
-    addr.sin_addr.s_addr = INADDR_ANY;
-    
-    reuse = 1;
-    setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
-    if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == -1) {
-		close (sock);
-		return -1;
-    }
-    
-    len = sizeof (addr);
-    if (getsockname (sock, (struct sockaddr *)&addr, &len) == -1) {
-		close (sock);
-		return -1;
-    }
-    
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
-    /* XXX This exposes a potential race condition, but without this,
-     * httpd will not start on the above listed platforms due to the fact
-     * that SO_REUSEADDR is also needed when Apache binds to the listening
-     * socket.  At this time, Apache does not support that socket option.
-     */
-    close (sock);
-#endif
-    return ntohs (addr.sin_port);
-}
-
-
-static char *
-get_share_name (void)
-{
-	static char *name = NULL;
-	const char *host_name;
-	
-	if (name == NULL) {
-		host_name = g_get_host_name ();
-		if (strcmp (host_name, "localhost") == 0) {
-			/* Translators: The %s will get filled in with the user name
-			   of the user, to form a genitive. If this is difficult to
-			   translate correctly so that it will work correctly in your
-			   language, you may use something equivalent to
-			   "Public files of %s", or leave out the %s altogether.
-			   In the latter case, please put "%.0s" somewhere in the string,
-			   which will match the user name string passed by the C code,
-			   but not put the user name in the final string. This is to
-			   avoid the warning that msgfmt might otherwise generate. */
-			name = g_strdup_printf (_("%s's public files"), g_get_user_name ());
-		} else {
-			/* Translators: This is similar to the string before, only it
-			   has the hostname in it too. */
-			name = g_strdup_printf (_("%s's public files on %s"),
-									g_get_user_name (),
-									host_name);
-		}
-			
-	}
-	return name;
-}
-
-#ifdef HAVE_DBUS_1_1
 static void
-init_dbus() {
-	/* The only use we make of D-BUS is to fetch the session BUS ID so we can export
-	 * it via mDNS, so we connect and then immediately disconnect. If we were using
-	 * the D-BUS session BUS for something persistent, the following code should use
-	 * dbus_bus_get() and skip the shutdown. (Avahi uses the D-BUS  _system_ bus
-	 * internally.)
-	 */
-	
-	DBusError derror;
-	DBusConnection *connection;
-
-	dbus_error_init(&derror);
-	
-	connection = dbus_bus_get_private(DBUS_BUS_SESSION, &derror);
-	if (connection == NULL) {
-        g_printerr("Failed to connect to session bus: %s", derror.message);
-        dbus_error_free(&derror);
-		return;
-	}
-
-    dbus_session_id = dbus_bus_get_id(connection, &derror);
-    if (dbus_session_id == NULL) {
-		/* This can happen if the D-BUS library has been upgraded to 1.1, but the
-		 * user's session hasn't yet been restarted
-		 */
-        g_printerr("Failed to get session BUS ID: %s", derror.message);
-        dbus_error_free(&derror);
-	}
-		
-	dbus_connection_set_exit_on_disconnect(connection, FALSE);
-	dbus_connection_close(connection);
-	dbus_connection_unref(connection);
-}
-#endif
-
-#ifdef HAVE_AVAHI
-
-static AvahiClient *avahi_client = NULL;
-static gboolean avahi_should_publish = FALSE;
-static gboolean avahi_running = FALSE;
-static int avahi_port = 0;
-static AvahiEntryGroup *entry_group = NULL;
-static char *avahi_name = NULL;
-
-static AvahiStringList*
-new_text_record_list (const char *first_key,
-					  const char *first_value,
-					  ...)
-{
-    va_list args;
-    const char *k;
-    const char *v;
-    AvahiStringList *list;
-
-    if (first_key == NULL)
-        return NULL;
-
-    list = NULL;
-    
-    list = avahi_string_list_add_pair (list, first_key, first_value);
-    
-    va_start (args, first_value);
-    k = va_arg (args, const char*);
-    if (k)
-        v = va_arg (args, const char*);
-    while (k != NULL) {
-        list = avahi_string_list_add_pair (list, k, v);
-        
-        k = va_arg (args, const char*);
-        if (k)
-            v = va_arg (args, const char*);
-    }
-    
-    va_end(args);
-    
-    return list;
-}
-
-static gboolean
-create_service (void) {
-    int ret;
-    AvahiStringList *txt_records;
-
-	if (avahi_name == NULL) {
-		avahi_name = g_strdup (get_share_name ());
-	}
-
-	txt_records = new_text_record_list ("u", "guest",
-#ifdef HAVE_DBUS_1_1
-										/* This must be last */
-										dbus_session_id != NULL ? "org.freedesktop.od.session" : NULL, dbus_session_id,
-#endif									   
-										NULL);
-	
-	ret = avahi_entry_group_add_service_strlst (entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 
-												 AVAHI_PUBLISH_USE_MULTICAST,
-												 avahi_name, "_webdav._tcp", NULL, NULL, 
-												 avahi_port,
-		                                         txt_records);
-
-	avahi_string_list_free(txt_records);
-	
-	if (ret < 0) {
-		return FALSE;
+require_password_changed (GConfClient* client,
+			  guint cnxn_id,
+			  GConfEntry *entry,
+			  gpointer data)
+{
+	/* Need to restart to get new password setting */
+	if (http_get_pid () != 0) {
+		http_down ();
+		http_up ();
 	}
-
-	ret = avahi_entry_group_commit (entry_group);
-
-    if (ret < 0) {
-        return FALSE;
-    }
-
-    return TRUE;
-}
-
-static void 
-entry_group_callback (AvahiEntryGroup *g, 
-					  AvahiEntryGroupState state, 
-					  void *userdata) 
-{
-    if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
-    } else if (state == AVAHI_ENTRY_GROUP_COLLISION) {
-        char *n;
-
-        /* A service name collision happened. Let's pick a new name */
-        n = avahi_alternative_service_name (avahi_name);
-        g_free (avahi_name);
-        avahi_name = n;
-
-        fprintf (stderr, "Service name collision, renaming service to '%s'\n", avahi_name);
-
-        /* And recreate the services */
-        create_service();
-    }
-}
-
-static void
-avahi_client_callback (AvahiClient *client, AvahiClientState state, void *userdata)
-{
-    if (state == AVAHI_CLIENT_S_RUNNING) {
-		avahi_running = TRUE;
-		if (avahi_should_publish) {
-			create_service ();
-		}
-	} else if (state == AVAHI_CLIENT_S_COLLISION) {
-		avahi_entry_group_reset (entry_group);
-    } else if (state == AVAHI_CLIENT_FAILURE) {
-		avahi_running = FALSE;
-    }
 }
 
+/* File sharing was disabled for some time, exit now */
+/* If we re-enable it in the ui, this will be restarted anyway */
 static gboolean
-init_avahi (void)
+disabled_timeout_callback (gpointer user_data)
 {
-	AvahiGLibPoll *poll;
-	int error;
+	GConfClient* client = (GConfClient *) user_data;
+	http_down ();
 
-    avahi_set_allocator (avahi_glib_allocator ());
-
-	poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT);
-
-	/* Create a new AvahiClient instance */
-	avahi_client = avahi_client_new (avahi_glib_poll_get (poll),
-									 AVAHI_CLIENT_NO_FAIL,
-									 avahi_client_callback,
-									 NULL,
-									 &error);
-	if (avahi_client == NULL) {
-		return FALSE;
-	}
-
-	entry_group = avahi_entry_group_new (avahi_client, entry_group_callback, NULL);
-	if (entry_group == NULL) {
-		return FALSE;
-	}
-	
-	return TRUE;
-}
-
-
-static gboolean
-publish_service (int port)
-{
-	avahi_should_publish = TRUE;
-	avahi_port = port;
-	if (avahi_running) {
-		create_service ();
-	}
-	return TRUE;
+	if (gconf_client_get_bool (client,
+				   FILE_SHARING_BLUETOOTH_ENABLED, NULL) == FALSE)
+		_exit (0);
+	return FALSE;
 }
 
 static void
-stop_publishing (void)
+file_sharing_enabled_changed (GConfClient* client,
+			      guint cnxn_id,
+			      GConfEntry *entry,
+			      gpointer data)
 {
-	avahi_should_publish = FALSE;
-	avahi_entry_group_reset (entry_group);
-}
-#endif
+	gboolean enabled;
 
-#ifdef HAVE_HOWL
-
-static sw_discovery_publish_id published_id = 0;
-static sw_discovery howl_session;
-
-static gboolean
-howl_input (GIOChannel  *io_channel,
-			GIOCondition cond,
-			gpointer     callback_data)
-{
-	sw_discovery session;
-	session = callback_data;
-	sw_salt salt;
-
-	if (sw_discovery_salt (session, &salt) == SW_OKAY) {
-		sw_salt_lock (salt);
-		sw_discovery_read_socket (session);
-		sw_salt_unlock (salt);
+	if (disabled_timeout_tag != 0) {
+		g_source_remove (disabled_timeout_tag);
+		disabled_timeout_tag = 0;
 	}
-	return TRUE;
-}
-
-static void
-set_up_howl_session (sw_discovery session)
-{
-	int fd;
-	GIOChannel *channel;
-
-	fd = sw_discovery_socket (session);
-
-	channel = g_io_channel_unix_new (fd);
-	g_io_add_watch (channel,
-					G_IO_IN,
-					howl_input, session);
-	g_io_channel_unref (channel);
-}
-
-static sw_result
-publish_reply (sw_discovery			discovery,
-			   sw_discovery_publish_status	status,
-			   sw_discovery_oid			id,
-			   sw_opaque			extra)
-{
-	return SW_OKAY;
-}
-
-
-static gboolean
-publish_service (int port)
-{
-    sw_result result;
 
-    result = sw_discovery_publish (howl_session, 0,
-								   get_share_name (),
-								   "_webdav._tcp",
-								   NULL, NULL,
-								   port,
-								   /* TODO: should be u=guest */
-								   /* text */ (unsigned char *) "", 0,
-								   publish_reply, NULL, &published_id);
-    if (result != SW_OKAY) {
-		return FALSE;
-    }
-    return TRUE;
+	enabled = gconf_client_get_bool (client,
+					 FILE_SHARING_ENABLED, NULL);
+	if (enabled) {
+		if (http_get_pid () == 0) {
+			http_up ();
+		}
+	} else {
+		http_down ();
+		disabled_timeout_tag = g_timeout_add (3*1000,
+						      (GSourceFunc)disabled_timeout_callback,
+						      client);
+	}
 }
 
 static void
-stop_publishing (void)
+file_sharing_bluetooth_allow_write_changed (GConfClient* client,
+					    guint cnxn_id,
+					    GConfEntry *entry,
+					    gpointer data)
 {
-    if (published_id != 0)
-		sw_discovery_cancel (howl_session, published_id);
-    published_id = 0;
+	obexftp_restart ();
 }
-#endif /* HAVE_HOWL */
-
 
 static void
-ensure_conf_dir (void)
+file_sharing_bluetooth_enabled_changed (GConfClient* client,
+					guint cnxn_id,
+					GConfEntry *entry,
+					gpointer data)
 {
-    char *dirname;
-
-    dirname = g_build_filename (g_get_home_dir (), ".gnome2", "user-share", NULL);
-    g_mkdir_with_parents (dirname, 0755);
-    g_free (dirname);
-}
-
-static void
-httpd_child_setup (gpointer user_data)
-{
-#ifdef HAVE_SELINUX
-	char *mycon;
-	/* If selinux is enabled, avoid transitioning to the httpd_t context,
-	   as this normally means you can't read the users homedir. */
-	if (is_selinux_enabled()) {
-		if (getcon (&mycon) < 0) {
-			abort ();
+	if (gconf_client_get_bool (client,
+				   FILE_SHARING_BLUETOOTH_ENABLED, NULL) == FALSE) {
+		obexftp_down ();
+		if (gconf_client_get_bool (client,
+					   FILE_SHARING_ENABLED, NULL) == FALSE) {
+			_exit (0);
 		}
-		if (setexeccon (mycon) < 0)
-			abort ();
-		freecon (mycon);
+	} else {
+		obexftp_up ();
 	}
-#endif
 }
 
-static gboolean
-spawn_httpd (int port, pid_t *pid_out)
-{
-    char *free1, *free2, *free3;
-    gboolean res;
-    char *argv[10];
-    char *env[10];
-    int i;
-    gint status;
-    char *pid_filename;
-    char *pidfile;
-    GError *error;
-    gboolean got_pidfile;
-    GConfClient *client;
-    char *str;
-    char *public_dir;
-
-    public_dir = lookup_public_dir ();
-    ensure_conf_dir ();
-
-    i = 0;
-    argv[i++] = HTTPD_PROGRAM;
-    argv[i++] = "-f";
-    argv[i++] = HTTPD_CONFIG;
-    argv[i++] = "-C";
-    free1 = argv[i++] = g_strdup_printf ("Listen %d", port);
-
-    client = gconf_client_get_default ();
-    str = gconf_client_get_string (client,
-				   FILE_SHARING_REQUIRE_PASSWORD, NULL);
-
-    if (str && strcmp (str, "never") == 0) {
-		/* Do nothing */
-    } else if (str && strcmp (str, "on_write") == 0){
-		argv[i++] = "-D";
-		argv[i++] = "RequirePasswordOnWrite";
-    } else {
-		/* always, or safe fallback */
-		argv[i++] = "-D";
-		argv[i++] = "RequirePasswordAlways";
-    }
-    g_free (str);
-    g_object_unref (client);
-    
-    argv[i] = NULL;
-
-    i = 0;
-    free2 = env[i++] = g_strdup_printf("HOME=%s", g_get_home_dir());
-    free3 = env[i++] = g_strdup_printf("XDG_PUBLICSHARE_DIR=%s", public_dir);
-    env[i++] = "LANG=C";
-    env[i] = NULL;
-
-    pid_filename = g_build_filename (g_get_home_dir (), ".gnome2/user-share/pid", NULL);
-
-    /* Remove pid file before spawning to avoid races with child and old pidfile */
-    unlink (pid_filename);
-    
-    error = NULL;
-    res = g_spawn_sync (g_get_home_dir(),
-			argv, env, 0,
-			httpd_child_setup, NULL,
-			NULL, NULL,
-			&status,
-			&error);
-    g_free (free1);
-    g_free (free2);
-    g_free (free3);
-    g_free (public_dir);
-
-    if (!res) {
-		fprintf (stderr, "error spawning httpd: %s\n",
-				 error->message);
-		g_error_free (error);
-		return FALSE;
-    }
-
-    if (status != 0) {
-		g_free (pid_filename);
-		return FALSE;
-    }
-
-    got_pidfile = FALSE;
-    error = NULL;
-    for (i = 0; i < 5; i++) {
-	if (error != NULL) 
-	    g_error_free (error); 
-	error = NULL;
-	if (g_file_get_contents (pid_filename, &pidfile, NULL, &error)) {
-	    got_pidfile = TRUE;
-	    *pid_out = atoi (pidfile);
-	    g_free (pidfile);
-	    break;
-	}
-	sleep (1);
-    }
-    
-    g_free (pid_filename);
-    
-    if (!got_pidfile) {
-		fprintf (stderr, "error opening httpd pidfile: %s\n", error->message);
-		g_error_free (error);
-		return FALSE;
-    }
-    return TRUE;
-}
-
-static void
-kill_httpd (void)
-{
-    if (httpd_pid != 0) {
-		kill (httpd_pid, SIGTERM);
-	
-		/* Allow child time to die, we can't waitpid, because its
-		   not a direct child */
-		sleep (1);
-    }
-    httpd_pid = 0;
-}
-
-
 static RETSIGTYPE
 cleanup_handler (int sig)
 {
-    kill_httpd ();
-    _exit (2);
-}
-
-/* File sharing was disabled for some time, exit now */
-/* If we re-enable it in the ui, this will be restarted anyway */
-static gboolean
-disabled_timeout_callback (void)
-{
-    kill_httpd ();
-    exit (0);
-    
-    return FALSE;
-}
-
-static void
-up (void)
-{
-    guint port;
-    
-    port = get_port ();
-    if (!spawn_httpd (port, &httpd_pid)) {
-		fprintf (stderr, "spawning httpd failed\n");
-    } else {
-		if (!publish_service (port)) {
-			fprintf (stderr, "publishing failed\n");
-		}
-    }
-}
-
-static void
-down (void)
-{
-    kill_httpd ();
-    stop_publishing ();
-}
-
-static void
-require_password_changed (GConfClient* client,
-						  guint cnxn_id,
-						  GConfEntry *entry,
-						  gpointer data)
-{
-    /* Need to restart to get new password setting */
-    if (httpd_pid != 0) {
-		down ();
-		up ();
-    }
-}
-
-static void
-file_sharing_enabled_changed (GConfClient* client,
-							  guint cnxn_id,
-							  GConfEntry *entry,
-							  gpointer data)
-{
-    gboolean enabled;
-    
-    if (disabled_timeout_tag != 0) {
-		g_source_remove (disabled_timeout_tag);
-		disabled_timeout_tag = 0;
-    }
-	
-    enabled = gconf_client_get_bool (client,
-									 FILE_SHARING_ENABLED, NULL);
-    if (enabled) {
-		if (httpd_pid == 0) {
-			up ();
-		}
-    } else {
-		down ();
-		disabled_timeout_tag = g_timeout_add (3*1000,
-											  (GSourceFunc)disabled_timeout_callback,
-											  NULL);
-    }
+	http_down ();
+	obexftp_down ();
+	_exit (2);
 }
 
 static int
 x_io_error_handler (Display *xdisplay)
 {
-    kill_httpd ();
-    _exit (2);
+	http_down ();
+	obexftp_down ();
+	_exit (2);
 }
 
 static gboolean
 x_input (GIOChannel  *io_channel,
-		 GIOCondition cond,
-		 gpointer     callback_data)
+	 GIOCondition cond,
+	 gpointer     callback_data)
 {
-    Display *xdisplay;
-    XEvent ignored;
+	Display *xdisplay;
+	XEvent ignored;
 
-    xdisplay = callback_data;
-    while (XPending (xdisplay)) {
+	xdisplay = callback_data;
+	while (XPending (xdisplay)) {
 		XNextEvent (xdisplay, &ignored);
-    }
-    return TRUE;
+	}
+	return TRUE;
 }
 
 int
 main (int argc, char **argv)
 {
-    GConfClient *client;
-    Display *xdisplay;
-    int x_fd;
-    GIOChannel *channel;
-    Window selection_owner;
-    Atom xatom;
-    
-    g_type_init ();
-    loop = g_main_loop_new (NULL, FALSE);
-    
-    signal (SIGPIPE, SIG_IGN);
-    signal (SIGINT, cleanup_handler);
-    signal (SIGHUP, cleanup_handler);
-    signal (SIGTERM, cleanup_handler);
-    
-    xdisplay = XOpenDisplay (NULL);
-    if (xdisplay == NULL) {
+	GConfClient *client;
+	Display *xdisplay;
+	int x_fd;
+	GIOChannel *channel;
+	Window selection_owner;
+	Atom xatom;
+
+	g_type_init ();
+	loop = g_main_loop_new (NULL, FALSE);
+
+	signal (SIGPIPE, SIG_IGN);
+	signal (SIGINT, cleanup_handler);
+	signal (SIGHUP, cleanup_handler);
+	signal (SIGTERM, cleanup_handler);
+
+	xdisplay = XOpenDisplay (NULL);
+	if (xdisplay == NULL) {
 		fprintf (stderr, "Can't open display\n");
 		return 1;
-    }
-	
-    xatom = XInternAtom (xdisplay, "_GNOME_USER_SHARE", FALSE);
-    selection_owner = XGetSelectionOwner (xdisplay, xatom);
-	
-    if (selection_owner != None) {
+	}
+
+	xatom = XInternAtom (xdisplay, "_GNOME_USER_SHARE", FALSE);
+	selection_owner = XGetSelectionOwner (xdisplay, xatom);
+
+	if (selection_owner != None) {
 		/* There is an owner already, quit */
 		return 1;
-    }
-	
-    selection_owner = XCreateSimpleWindow (xdisplay,
-										   RootWindow (xdisplay, 0),
-										   0, 0, 1, 1,
-										   0, 0, 0);
-    XSetSelectionOwner (xdisplay, xatom, selection_owner, CurrentTime);
-	
-    if (XGetSelectionOwner (xdisplay, xatom) != selection_owner) {
+	}
+
+	selection_owner = XCreateSimpleWindow (xdisplay,
+					       RootWindow (xdisplay, 0),
+					       0, 0, 1, 1,
+					       0, 0, 0);
+	XSetSelectionOwner (xdisplay, xatom, selection_owner, CurrentTime);
+
+	if (XGetSelectionOwner (xdisplay, xatom) != selection_owner) {
 		/* Didn't get the selection */
 		return 1;
-    }
+	}
 
-    client = gconf_client_get_default ();
-    if (gconf_client_get_bool (client, FILE_SHARING_ENABLED, NULL) == FALSE)
-	    return 1;
-
-    x_fd = ConnectionNumber (xdisplay);
-    XSetIOErrorHandler (x_io_error_handler);
-    
-    channel = g_io_channel_unix_new (x_fd);
-    g_io_add_watch (channel,
-		    G_IO_IN,
-		    x_input, xdisplay);
-    g_io_channel_unref (channel);
-
-#ifdef HAVE_DBUS_1_1
-	init_dbus();
-#endif	
-	
-#ifdef HAVE_AVAHI
-	if (!init_avahi ()) {    
-		/* Print out the error string */
-		fprintf (stderr, "avahi init failed\n");
+	client = gconf_client_get_default ();
+	if (gconf_client_get_bool (client, FILE_SHARING_ENABLED, NULL) == FALSE &&
+	    gconf_client_get_bool (client, FILE_SHARING_BLUETOOTH_ENABLED, NULL) == FALSE)
 		return 1;
-	}
-#endif
-#ifdef HAVE_HOWL
-    if (sw_discovery_init (&howl_session) != SW_OKAY) {
-		fprintf (stderr, "howl init failed\n");
+
+	x_fd = ConnectionNumber (xdisplay);
+	XSetIOErrorHandler (x_io_error_handler);
+
+	channel = g_io_channel_unix_new (x_fd);
+	g_io_add_watch (channel,
+			G_IO_IN,
+			x_input, xdisplay);
+	g_io_channel_unref (channel);
+
+	if (http_init () == FALSE)
+		return 1;
+	if (obexftp_init () == FALSE)
 		return 1;
-    }
-    set_up_howl_session (howl_session);
-#endif
-
-    gconf_client_add_dir (client,
-			  FILE_SHARING_DIR,
-			  GCONF_CLIENT_PRELOAD_RECURSIVE,
-			  NULL);
-
-    gconf_client_notify_add (client,
-			     FILE_SHARING_ENABLED,
-			     file_sharing_enabled_changed,
-			     NULL,
-			     NULL,
-			     NULL);
-    gconf_client_notify_add (client,
-			     FILE_SHARING_REQUIRE_PASSWORD,
-			     require_password_changed,
-			     NULL,
-			     NULL,
-			     NULL);
-    g_object_unref (client);
-
-    /* Initial setting */
-    file_sharing_enabled_changed (client, 0, NULL, NULL);
-
-    g_main_loop_run (loop);
-    
-    return 0;
+
+	gconf_client_add_dir (client,
+			      FILE_SHARING_DIR,
+			      GCONF_CLIENT_PRELOAD_RECURSIVE,
+			      NULL);
+
+	gconf_client_notify_add (client,
+				 FILE_SHARING_ENABLED,
+				 file_sharing_enabled_changed,
+				 NULL,
+				 NULL,
+				 NULL);
+	gconf_client_notify_add (client,
+				 FILE_SHARING_REQUIRE_PASSWORD,
+				 require_password_changed,
+				 NULL,
+				 NULL,
+				 NULL);
+	gconf_client_notify_add (client,
+				 FILE_SHARING_BLUETOOTH_ENABLED,
+				 file_sharing_bluetooth_enabled_changed,
+				 NULL,
+				 NULL,
+				 NULL);
+	gconf_client_notify_add (client,
+				 FILE_SHARING_BLUETOOTH_ALLOW_WRITE,
+				 file_sharing_bluetooth_allow_write_changed,
+				 NULL,
+				 NULL,
+				 NULL);
+	g_object_unref (client);
+
+	/* Initial setting */
+	file_sharing_enabled_changed (client, 0, NULL, NULL);
+	file_sharing_bluetooth_enabled_changed (client, 0, NULL, NULL);
+
+	g_main_loop_run (loop);
+
+	return 0;
 }
+

Added: trunk/user_share.h
==============================================================================
--- (empty file)
+++ trunk/user_share.h	Tue Jan 22 12:54:01 2008
@@ -0,0 +1,31 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+ *  Copyright (C) 2004-2008 Red Hat, Inc.
+ *
+ *  Nautilus is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Nautilus is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Authors: Alexander Larsson <alexl redhat com>
+ *
+ */
+
+#define FILE_SHARING_DIR "/desktop/gnome/file_sharing"
+#define FILE_SHARING_ENABLED "/desktop/gnome/file_sharing/enabled"
+#define FILE_SHARING_BLUETOOTH_ENABLED "/desktop/gnome/file_sharing/bluetooth_enabled"
+#define FILE_SHARING_REQUIRE_PASSWORD "/desktop/gnome/file_sharing/require_password"
+#define FILE_SHARING_BLUETOOTH_ALLOW_WRITE "/desktop/gnome/file_sharing/bluetooth_allow_write"
+
+char *lookup_public_dir (void);
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]