Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.2:Test
gnome-vfs2
gnome-vfs2-net-usershare.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gnome-vfs2-net-usershare.diff of Package gnome-vfs2
Index: modules/Makefile.am ================================================================================ --- modules/Makefile.am +++ modules/Makefile.am @@ -140,7 +140,7 @@ ### `network' method -libnetwork_la_SOURCES = network-method.c +libnetwork_la_SOURCES = network-method.c shares.c shares.h libnetwork_la_LDFLAGS = $(module_flags) libnetwork_la_LIBADD = $(MODULES_FILE_LIBS) ../libgnomevfs/libgnomevfs-2.la --- modules/network-method.c +++ modules/network-method.c @@ -40,6 +40,8 @@ #include <glib/gi18n-lib.h> +#include "shares.h" + #define PATH_GCONF_GNOME_VFS_SMB "/system/smb" #define PATH_GCONF_GNOME_VFS_SMB_WORKGROUP "/system/smb/workgroup" #define PATH_GCONF_GNOME_VFS_DNS_SD "/system/dns_sd" @@ -52,6 +54,7 @@ char *icon; char *target_uri; char *filename; + char *desktop_item_type; } NetworkLink; typedef struct { @@ -84,6 +87,7 @@ static GList *current_dns_sd_domains = NULL; +static GSList *current_shares = NULL; static GList *active_links; static GList *active_redirects; @@ -152,26 +156,6 @@ return NETWORK_LOCAL_DISABLED; } -static char * -get_data_for_link (const char *uri, - const char *display_name, - const char *icon) -{ - char *data; - - data = g_strdup_printf ("[Desktop Entry]\n" - "Encoding=UTF-8\n" - "Name=%s\n" - "Type=FSDevice\n" - "Icon=%s\n" - "URL=%s\n", - display_name, - icon, - uri); - return data; -} - - /* Called with lock held */ static void do_link_event (const char *filename, @@ -227,6 +211,7 @@ g_free (found->target_uri); g_free (found->display_name); g_free (found->icon); + g_free (found->desktop_item_type); g_free (found); } } @@ -236,7 +221,8 @@ add_link (const char *filename, const char *target_uri, const char *display_name, - const char *icon) + const char *icon, + const char *desktop_item_type) { NetworkLink *link; @@ -245,6 +231,7 @@ link->target_uri = g_strdup (target_uri); link->display_name = g_strdup (display_name); link->icon = g_strdup (icon); + link->desktop_item_type = g_strdup (desktop_item_type); active_links = g_list_prepend (active_links, link); do_link_event (filename, @@ -265,7 +252,8 @@ add_link (filename, link_uri, domain, - "gnome-fs-network"); + "gnome-fs-network", + "FSDevice"); g_free (filename); g_free (link_uri); } @@ -355,9 +343,20 @@ static char * network_link_create_data (NetworkLink *link) { - return get_data_for_link (link->target_uri, - link->display_name, - link->icon); + char *data; + + data = g_strdup_printf ("[Desktop Entry]\n" + "Encoding=UTF-8\n" + "Name=%s\n" + "Type=%s\n" + "Icon=%s\n" + "URL=%s\n", + link->display_name, + link->desktop_item_type, + link->icon, + link->target_uri); + + return data; } /* Call with lock held */ @@ -754,6 +753,18 @@ dir_handle->filenames = g_list_prepend (dir_handle->filenames, g_strdup (filename)); } +static char * +build_fake_share_filename (ShareInfo *share_info) +{ + return g_strdup_printf ("share-%s", share_info->share_name); +} + +static char * +build_share_target_uri (ShareInfo *share_info) +{ + return gnome_vfs_get_uri_from_local_path (share_info->path); +} + static GnomeVFSResult do_open_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, @@ -1262,6 +1273,104 @@ } static void +diff_sorted_slists (GSList *list1, GSList *list2, GCompareFunc compare, + GSList **added, GSList **removed) +{ + int order; + + *added = *removed = NULL; + + while (list1 != NULL && + list2 != NULL) { + order = (*compare) (list1->data, list2->data); + if (order < 0) { + *removed = g_slist_prepend (*removed, list1->data); + list1 = list1->next; + } else if (order > 0) { + *added = g_slist_prepend (*added, list2->data); + list2 = list2->next; + } else { /* same item */ + list1 = list1->next; + list2 = list2->next; + } + } + + while (list1 != NULL) { + *removed = g_slist_prepend (*removed, list1->data); + list1 = list1->next; + } + while (list2 != NULL) { + *added = g_slist_prepend (*added, list2->data); + list2 = list2->next; + } +} + +static gint +compare_share_info_name_cb (gconstpointer a, gconstpointer b) +{ + const ShareInfo *share_a, *share_b; + + share_a = a; + share_b = b; + + return g_utf8_collate (share_a->share_name, share_b->share_name); +} + +/* Call with the lock held */ +static void +refresh_shares_links (void) +{ + GSList *share_info_list; + GSList *l; + GSList *added; + GSList *removed; + + G_LOCK (network); + + /* FIXME: NULL GError */ + if (!shares_get_share_info_list (&share_info_list, NULL)) { + goto out; + } + + share_info_list = g_slist_sort (share_info_list, compare_share_info_name_cb); + diff_sorted_slists (current_shares, share_info_list, compare_share_info_name_cb, &added, &removed); + + for (l = removed; l; l = l->next) { + ShareInfo *share_info; + char *filename; + + share_info = l->data; + filename = build_fake_share_filename (share_info); + remove_link (filename); + g_free (filename); + } + + for (l = added; l; l = l->next) { + ShareInfo *share_info; + char *filename; + char *target_uri; + + share_info = l->data; + + filename = build_fake_share_filename (share_info); + target_uri = build_share_target_uri (share_info); + add_link (filename, target_uri, share_info->share_name, "gnome-fs-directory", "Link"); + g_free (filename); + g_free (target_uri); + } + + g_slist_free (added); + g_slist_free (removed); + + shares_free_share_info_list (current_shares); + current_shares = share_info_list; + + out: + + G_UNLOCK (network); +} + +static void refresh_link_lists (void) { char hostname[256]; @@ -1330,6 +1439,8 @@ G_UNLOCK (network); } + + refresh_shares_links (); } @@ -1397,7 +1508,8 @@ add_link ("smblink-root", "smb://", _("Windows Network"), - "gnome-fs-network"); + "gnome-fs-network", + "FSDevice"); } --- modules/shares.c +++ modules/shares.c @@ -0,0 +1,890 @@ +#include <config.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <glib/gi18n-lib.h> +#include "shares.h" + +#undef DEBUG_SHARES +#ifdef DEBUG_SHARES +# define NET_USERSHARE_ARGV0 "debug-net-usershare" +#else +# define NET_USERSHARE_ARGV0 "net" +#endif + +static GHashTable *path_share_info_hash; +static GHashTable *share_name_share_info_hash; + +#define NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES 100 +#define TIMESTAMP_THRESHOLD 10 /* seconds */ +static int refresh_timestamp_update_counter; +static time_t refresh_timestamp; + +#define KEY_PATH "path" +#define KEY_COMMENT "comment" +#define KEY_ACL "usershare_acl" + +/* Debugging flags */ +static gboolean throw_error_on_refresh; +static gboolean throw_error_on_add; +static gboolean throw_error_on_modify; +static gboolean throw_error_on_remove; + + + +/* Interface to "net usershare" */ + +static gboolean +net_usershare_run (int argc, char **argv, GKeyFile **ret_key_file, GError **error) +{ + int real_argc; + int i; + char **real_argv; + gboolean retval; + char *stdout_contents; + char *stderr_contents; + int exit_status; + int exit_code; + GKeyFile *key_file; + GError *real_error; + + g_assert (argc > 0); + g_assert (argv != NULL); + g_assert (error == NULL || *error == NULL); + + if (ret_key_file) + *ret_key_file = NULL; + + /* Build command line */ + + real_argc = 2 + argc + 1; /* "net" "usershare" [argv] NULL */ + real_argv = g_new (char *, real_argc); + + real_argv[0] = NET_USERSHARE_ARGV0; + real_argv[1] = "usershare"; + + for (i = 0; i < argc; i++) { + g_assert (argv[i] != NULL); + real_argv[i + 2] = argv[i]; + } + + real_argv[real_argc - 1] = NULL; + + /* Launch */ + + stdout_contents = NULL; + stderr_contents = NULL; + + { + char **p; + + g_message ("------------------------------------------"); + + for (p = real_argv; *p; p++) + g_message ("spawn arg \"%s\"", *p); + + g_message ("end of spawn args; SPAWNING\n"); + } + + real_error = NULL; + retval = g_spawn_sync (NULL, /* cwd */ + real_argv, + NULL, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* GSpawnChildSetupFunc */ + NULL, /* user_data */ + &stdout_contents, + &stderr_contents, + &exit_status, + &real_error); + + g_message ("returned from spawn: %s: %s", retval ? "SUCCESS" : "FAIL", retval ? "" : real_error->message); + + if (!retval) { + g_propagate_error (error, real_error); + goto out; + } + + if (!WIFEXITED (exit_status)) { + g_message ("WIFEXITED(%d) was false!", exit_status); + retval = FALSE; + + if (WIFSIGNALED (exit_status)) { + int signal_num; + + signal_num = WTERMSIG (exit_status); + g_message ("Child got signal %d", signal_num); + + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + _("%s %s %s returned with signal %d"), + real_argv[0], + real_argv[1], + real_argv[2], + signal_num); + } else + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + _("%s %s %s failed for an unknown reason"), + real_argv[0], + real_argv[1], + real_argv[2]); + + goto out; + } + + exit_code = WEXITSTATUS (exit_status); + + g_message ("exit code %d", exit_code); + if (exit_code != 0) { + char *str; + char *message; + + /* stderr_contents is in the system locale encoding, not UTF-8 */ + + str = g_locale_to_utf8 (stderr_contents, -1, NULL, NULL, NULL); + + if (str && str[0]) + message = g_strdup_printf (_("'net usershare' returned error %d: %s"), exit_code, str); + else + message = g_strdup_printf (_("'net usershare' returned error %d"), exit_code); + + g_free (str); + + g_set_error (error, + G_SPAWN_ERROR, + G_SPAWN_ERROR_FAILED, + "%s", + message); + + g_free (message); + + retval = FALSE; + goto out; + } + + if (ret_key_file) { + g_message ("caller wants GKeyFile"); + + *ret_key_file = NULL; + + /* FIXME: jeallison@novell.com says the output of "net usershare" is nearly always + * in UTF-8, but that it can be configured in the master smb.conf. We assume + * UTF-8 for now. + */ + + if (!g_utf8_validate (stdout_contents, -1, NULL)) { + g_message ("stdout of net usershare was not in valid UTF-8"); + g_set_error (error, + G_SPAWN_ERROR, + G_SPAWN_ERROR_FAILED, + _("the output of 'net usershare' is not in valid UTF-8 encoding")); + retval = FALSE; + goto out; + } + + key_file = g_key_file_new (); + + real_error = NULL; + if (!g_key_file_load_from_data (key_file, stdout_contents, -1, 0, &real_error)) { + g_message ("Error when parsing key file {\n%s\n}: %s", stdout_contents, real_error->message); + g_propagate_error (error, real_error); + g_key_file_free (key_file); + retval = FALSE; + goto out; + } + + retval = TRUE; + *ret_key_file = key_file; + } else + retval = TRUE; + + g_message ("success from calling net usershare and parsing its output"); + + out: + g_free (real_argv); + g_free (stdout_contents); + g_free (stderr_contents); + + g_message ("------------------------------------------"); + + return retval; +} + + + +/* Internals */ + +static void +ensure_hashes (void) +{ + if (path_share_info_hash == NULL) { + g_assert (share_name_share_info_hash == NULL); + + path_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal); + share_name_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal); + } else + g_assert (share_name_share_info_hash != NULL); +} + +static ShareInfo * +lookup_share_by_path (const char *path) +{ + ensure_hashes (); + return g_hash_table_lookup (path_share_info_hash, path); +} + +static ShareInfo * +lookup_share_by_share_name (const char *share_name) +{ + ensure_hashes (); + return g_hash_table_lookup (share_name_share_info_hash, share_name); +} + +static void +add_share_info_to_hashes (ShareInfo *info) +{ + ensure_hashes (); + g_hash_table_insert (path_share_info_hash, info->path, info); + g_hash_table_insert (share_name_share_info_hash, info->share_name, info); +} + +static void +remove_share_info_from_hashes (ShareInfo *info) +{ + ensure_hashes (); + g_hash_table_remove (path_share_info_hash, info->path); + g_hash_table_remove (share_name_share_info_hash, info->share_name); +} + +static gboolean +remove_from_path_hash_cb (gpointer key, + gpointer value, + gpointer data) +{ + ShareInfo *info; + + info = value; + shares_free_share_info (info); + + return TRUE; +} + +static gboolean +remove_from_share_name_hash_cb (gpointer key, + gpointer value, + gpointer data) +{ + /* The ShareInfo was already freed in remove_from_path_hash_cb() */ + return TRUE; +} + +static void +free_all_shares (void) +{ + ensure_hashes (); + g_hash_table_foreach_remove (path_share_info_hash, remove_from_path_hash_cb, NULL); + g_hash_table_foreach_remove (share_name_share_info_hash, remove_from_share_name_hash_cb, NULL); +} + +static char * +get_string_from_key_file (GKeyFile *key_file, const char *group, const char *key) +{ + GError *error; + char *str; + + error = NULL; + str = NULL; + + if (g_key_file_has_key (key_file, group, key, &error)) { + str = g_key_file_get_string (key_file, group, key, &error); + if (!str) { + g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND) + && !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)); + + g_error_free (error); + } + } else { + g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)); + g_error_free (error); + } + + return str; +} + +static void +add_key_group_to_hashes (GKeyFile *key_file, const char *group) +{ + char *path; + char *comment; + char *acl; + gboolean is_writable; + ShareInfo *info; + ShareInfo *old_info; + + /* Remove the old share based on the name */ + + old_info = lookup_share_by_share_name (group); + if (old_info) { + remove_share_info_from_hashes (old_info); + shares_free_share_info (old_info); + } + + /* Start parsing, and remove the old share based on the path */ + + path = get_string_from_key_file (key_file, group, KEY_PATH); + if (!path) { + g_message ("group '%s' doesn't have a '%s' key! Ignoring group.", group, KEY_PATH); + return; + } + + old_info = lookup_share_by_path (path); + if (old_info) { + remove_share_info_from_hashes (old_info); + shares_free_share_info (old_info); + } + + /* Finish parsing */ + + comment = get_string_from_key_file (key_file, group, KEY_COMMENT); + + acl = get_string_from_key_file (key_file, group, KEY_ACL); + if (acl) { + if (strcmp (acl, "Everyone:R") == 0) + is_writable = FALSE; + else if (strcmp (acl, "Everyone:F") == 0) + is_writable = TRUE; + else { + g_message ("unknown format for key '%s/%s' as it contains '%s'. Assuming that the share is read-only", + group, KEY_ACL, acl); + is_writable = FALSE; + } + + g_free (acl); + } else { + g_message ("group '%s' doesn't have a '%s' key! Assuming that the share is read-only.", group, KEY_ACL); + is_writable = FALSE; + } + + g_assert (path != NULL); + g_assert (group != NULL); + + info = g_new (ShareInfo, 1); + info->path = path; + info->share_name = g_strdup (group); + info->comment = comment; + info->is_writable = is_writable; + + add_share_info_to_hashes (info); +} + +static void +replace_shares_from_key_file (GKeyFile *key_file) +{ + gsize num_groups; + char **group_names; + gsize i; + + group_names = g_key_file_get_groups (key_file, &num_groups); + + /* FIXME: In add_key_group_to_hashes(), we simply ignore key groups + * which have invalid data (i.e. no path). We could probably accumulate a + * GError with the list of invalid groups and propagate it upwards. + */ + for (i = 0; i < num_groups; i++) { + g_assert (group_names[i] != NULL); + add_key_group_to_hashes (key_file, group_names[i]); + } + + g_strfreev (group_names); +} + +static gboolean +refresh_shares (GError **error) +{ + GKeyFile *key_file; + char *argv[1]; + GError *real_error; + + free_all_shares (); + + if (throw_error_on_refresh) { + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + _("Failed")); + return FALSE; + } + + argv[0] = "info"; + + real_error = NULL; + if (!net_usershare_run (G_N_ELEMENTS (argv), argv, &key_file, &real_error)) { + g_message ("Called \"net usershare info\" but it failed: %s", real_error->message); + g_propagate_error (error, real_error); + return FALSE; + } + + g_assert (key_file != NULL); + + replace_shares_from_key_file (key_file); + g_key_file_free (key_file); + + return TRUE; +} + +static gboolean +refresh_if_needed (GError **error) +{ + gboolean retval; + + if (refresh_timestamp_update_counter == 0) { + time_t new_timestamp; + + refresh_timestamp_update_counter = NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES; + + new_timestamp = time (NULL); + if (new_timestamp - refresh_timestamp > TIMESTAMP_THRESHOLD) { + g_message ("REFRESHING SHARES"); + retval = refresh_shares (error); + } else + retval = TRUE; + + refresh_timestamp = new_timestamp; + } else { + refresh_timestamp_update_counter--; + retval = TRUE; + } + + return retval; +} + +static ShareInfo * +copy_share_info (ShareInfo *info) +{ + ShareInfo *copy; + + if (!info) + return NULL; + + copy = g_new (ShareInfo, 1); + copy->path = g_strdup (info->path); + copy->share_name = g_strdup (info->share_name); + copy->comment = g_strdup (info->comment); + copy->is_writable = info->is_writable; + + return copy; +} + +static gboolean +add_share (ShareInfo *info, GError **error) +{ + char *argv[6]; + ShareInfo *copy; + GKeyFile *key_file; + GError *real_error; + + g_message ("add_share() start"); + + if (throw_error_on_add) { + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + _("Failed")); + g_message ("add_share() end FAIL"); + return FALSE; + } + + argv[0] = "add"; + argv[1] = "-l"; + argv[2] = info->share_name; + argv[3] = info->path; + argv[4] = info->comment; + argv[5] = info->is_writable ? "Everyone:F" : "Everyone:R"; + + real_error = NULL; + if (!net_usershare_run (G_N_ELEMENTS (argv), argv, &key_file, &real_error)) { + g_message ("Called \"net usershare add\" but it failed: %s", real_error->message); + g_propagate_error (error, real_error); + return FALSE; + } + + replace_shares_from_key_file (key_file); + + copy = copy_share_info (info); + add_share_info_to_hashes (copy); + + g_message ("add_share() end SUCCESS"); + + return TRUE; +} + +static gboolean +remove_share (const char *path, GError **error) +{ + ShareInfo *old_info; + char *argv[2]; + GError *real_error; + + g_message ("remove_share() start"); + + if (throw_error_on_remove) { + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + "Failed"); + g_message ("remove_share() end FAIL"); + return FALSE; + } + + old_info = lookup_share_by_path (path); + if (!old_info) { + char *display_name; + + display_name = g_filename_display_name (path); + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_NONEXISTENT, + _("Cannot remove the share for path %s: that path is not shared"), + display_name); + g_free (display_name); + + g_message ("remove_share() end FAIL: path %s was not in our hashes", path); + return FALSE; + } + + argv[0] = "delete"; + argv[1] = old_info->share_name; + + real_error = NULL; + if (!net_usershare_run (G_N_ELEMENTS (argv), argv, NULL, &real_error)) { + g_message ("Called \"net usershare delete\" but it failed: %s", real_error->message); + g_propagate_error (error, real_error); + g_message ("remove_share() end FAIL"); + return FALSE; + } + + remove_share_info_from_hashes (old_info); + shares_free_share_info (old_info); + + g_message ("remove_share() end SUCCESS"); + + return TRUE; +} + +static gboolean +modify_share (const char *old_path, ShareInfo *info, GError **error) +{ + ShareInfo *old_info; + + g_message ("modify_share() start"); + + old_info = lookup_share_by_path (old_path); + if (old_info == NULL) { + g_message ("modify_share() end; calling add_share() instead"); + return add_share (info, error); + } + + g_assert (old_info != NULL); + + if (strcmp (info->path, old_info->path) != 0) { + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + _("Cannot change the path of an existing share; please remove the old share first and add a new one")); + g_message ("modify_share() end FAIL: tried to change the path in a share!"); + return FALSE; + } + + if (throw_error_on_modify) { + g_set_error (error, + SHARES_ERROR, + SHARES_ERROR_FAILED, + "Failed"); + g_message ("modify_share() end FAIL"); + return FALSE; + } + + /* Although "net usershare add" will modify an existing share if it has the same share name + * as the one that gets passed in, our semantics are different. We have a one-to-one mapping + * between paths and share names; "net usershare" supports a one-to-many mapping from paths + * to share names. So, we must first remove the old share and then add the new/modified one. + */ + + if (!remove_share (old_path, error)) { + g_message ("modify_share() end FAIL: error when removing old share"); + return FALSE; + } + + g_message ("modify_share() end: will call add_share() with the new share info"); + return add_share (info, error); +} + + + +/* Public API */ + +GQuark +shares_error_quark (void) +{ + static GQuark quark; + + if (quark == 0) + quark = g_quark_from_string ("nautilus-shares-error-quark"); /* not from_static_string since we are a module */ + + return quark; +} + +/** + * shares_free_share_info: + * @info: A #ShareInfo structure. + * + * Frees a #ShareInfo structure. + **/ +void +shares_free_share_info (ShareInfo *info) +{ + g_assert (info != NULL); + + g_free (info->path); + g_free (info->share_name); + g_free (info->comment); + g_free (info); +} + +/** + * shares_get_path_is_shared: + * @path: A full path name ("/foo/bar/baz") in file system encoding. + * @ret_is_shared: Location to store result value (#TRUE if the path is shared, #FALSE otherwise) + * @error: Location to store error, or #NULL. + * + * Checks whether a path is shared through Samba. + * + * Return value: #TRUE if the info could be queried successfully, #FALSE + * otherwise. If this function returns #FALSE, an error code will be returned in the + * @error argument, and *@ret_is_shared will be set to #FALSE. + **/ +gboolean +shares_get_path_is_shared (const char *path, gboolean *ret_is_shared, GError **error) +{ + g_assert (ret_is_shared != NULL); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) { + *ret_is_shared = FALSE; + return FALSE; + } + + *ret_is_shared = (lookup_share_by_path (path) != NULL); + + return TRUE; +} + +/** + * shares_get_share_info_for_path: + * @path: A full path name ("/foo/bar/baz") in file system encoding. + * @ret_share_info: Location to store result with the share's info - on return, + * will be non-NULL if the path is indeed shared, or #NULL if the path is not + * shared. You must free the non-NULL value with shares_free_share_info(). + * @error: Location to store error, or #NULL. + * + * Queries the information for a shared path: its share name, its read-only status, etc. + * + * Return value: #TRUE if the info could be queried successfully, #FALSE + * otherwise. If this function returns #FALSE, an error code will be returned in the + * @error argument, and *@ret_share_info will be set to #NULL. + **/ +gboolean +shares_get_share_info_for_path (const char *path, ShareInfo **ret_share_info, GError **error) +{ + ShareInfo *info; + + g_assert (path != NULL); + g_assert (ret_share_info != NULL); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) { + *ret_share_info = NULL; + return FALSE; + } + + info = lookup_share_by_path (path); + *ret_share_info = copy_share_info (info); + + return TRUE; +} + +/** + * shares_get_share_name_exists: + * @share_name: Name of a share. + * @ret_exists: Location to store return value; #TRUE if the share name exists, #FALSE otherwise. + * + * Queries whether a share name already exists in the user's list of shares. + * + * Return value: #TRUE if the info could be queried successfully, #FALSE + * otherwise. If this function returns #FALSE, an error code will be returned in the + * @error argument, and *@ret_exists will be set to #FALSE. + **/ +gboolean +shares_get_share_name_exists (const char *share_name, gboolean *ret_exists, GError **error) +{ + g_assert (share_name != NULL); + g_assert (ret_exists != NULL); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) { + *ret_exists = FALSE; + return FALSE; + } + + *ret_exists = (lookup_share_by_share_name (share_name) != NULL); + + return TRUE; +} + +/** + * shares_get_share_info_for_share_name: + * @share_name: Name of a share. + * @ret_share_info: Location to store result with the share's info - on return, + * will be non-NULL if there is a share for the specified name, or #NULL if no + * share has such name. You must free the non-NULL value with + * shares_free_share_info(). + * @error: Location to store error, or #NULL. + * + * Queries the information for the share which has a specific name. + * + * Return value: #TRUE if the info could be queried successfully, #FALSE + * otherwise. If this function returns #FALSE, an error code will be returned in the + * @error argument, and *@ret_share_info will be set to #NULL. + **/ +gboolean +shares_get_share_info_for_share_name (const char *share_name, ShareInfo **ret_share_info, GError **error) +{ + ShareInfo *info; + + g_assert (share_name != NULL); + g_assert (ret_share_info != NULL); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) { + *ret_share_info = NULL; + return FALSE; + } + + info = lookup_share_by_share_name (share_name); + *ret_share_info = copy_share_info (info); + + return TRUE; +} + +/** + * shares_modify_share: + * @old_path: Path of the share to modify, or %NULL. + * @info: Info of the share to modify/add, or %NULL to delete a share. + * @error: Location to store error, or #NULL. + * + * Can add, modify, or delete shares. To add a share, pass %NULL for @old_path, + * and a non-null @info. To modify a share, pass a non-null @old_path and + * non-null @info; in this case, @info->path must have the same contents as + * @old_path. To remove a share, pass a non-NULL @old_path and a %NULL @info. + * + * Return value: TRUE if the share could be modified, FALSE otherwise. If this returns + * FALSE, then the error information will be placed in @error. + **/ +gboolean +shares_modify_share (const char *old_path, ShareInfo *info, GError **error) +{ + g_assert ((old_path == NULL && info != NULL) + || (old_path != NULL && info == NULL) + || (old_path != NULL && info != NULL)); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) + return FALSE; + + if (old_path == NULL) + return add_share (info, error); + else if (info == NULL) + return remove_share (old_path, error); + else + return modify_share (old_path, info, error); +} + +static void +copy_to_slist_cb (gpointer key, gpointer value, gpointer data) +{ + ShareInfo *info; + ShareInfo *copy; + GSList **list; + + info = value; + list = data; + + copy = copy_share_info (info); + *list = g_slist_prepend (*list, copy); +} + +/** + * shares_get_share_info_list: + * @ret_info_list: Location to store the return value, which is a list + * of #ShareInfo structures. Free this with shares_free_share_info_list(). + * @error: Location to store error, or #NULL. + * + * Gets the list of shared folders and their information. + * + * Return value: #TRUE if the info could be queried successfully, #FALSE + * otherwise. If this function returns #FALSE, an error code will be returned in the + * @error argument, and *@ret_info_list will be set to #NULL. + **/ +gboolean +shares_get_share_info_list (GSList **ret_info_list, GError **error) +{ + g_assert (ret_info_list != NULL); + g_assert (error == NULL || *error == NULL); + + if (!refresh_if_needed (error)) { + *ret_info_list = NULL; + return FALSE; + } + + *ret_info_list = NULL; + g_hash_table_foreach (path_share_info_hash, copy_to_slist_cb, ret_info_list); + + return TRUE; +} + +/** + * shares_free_share_info_list: + * @list: List of #ShareInfo structures, or %NULL. + * + * Frees a list of #ShareInfo structures as returned by shares_get_share_info_list(). + **/ +void +shares_free_share_info_list (GSList *list) +{ + GSList *l; + + for (l = list; l; l = l->next) { + ShareInfo *info; + + info = l->data; + shares_free_share_info (l->data); + } + + g_slist_free (list); +} + +void +shares_set_debug (gboolean error_on_refresh, + gboolean error_on_add, + gboolean error_on_modify, + gboolean error_on_remove) +{ + throw_error_on_refresh = error_on_refresh; + throw_error_on_add = error_on_add; + throw_error_on_modify = error_on_modify; + throw_error_on_remove = error_on_remove; +} --- modules/shares.h +++ modules/shares.h @@ -0,0 +1,43 @@ +#ifndef SHARES_H +#define SHARES_H + +#include <glib.h> + +typedef struct { + char *path; + char *share_name; + char *comment; + gboolean is_writable; +} ShareInfo; + +#define SHARES_ERROR (shares_error_quark ()) + +typedef enum { + SHARES_ERROR_FAILED, + SHARES_ERROR_NONEXISTENT +} SharesError; + +GQuark shares_error_quark (void); + +void shares_free_share_info (ShareInfo *info); + +gboolean shares_get_path_is_shared (const char *path, gboolean *ret_is_shared, GError **error); + +gboolean shares_get_share_info_for_path (const char *path, ShareInfo **ret_share_info, GError **error); + +gboolean shares_get_share_name_exists (const char *share_name, gboolean *ret_exists, GError **error); + +gboolean shares_get_share_info_for_share_name (const char *share_name, ShareInfo **ret_share_info, GError **error); + +gboolean shares_modify_share (const char *old_path, ShareInfo *info, GError **error); + +gboolean shares_get_share_info_list (GSList **ret_info_list, GError **error); + +void shares_free_share_info_list (GSList *list); + +void shares_set_debug (gboolean error_on_refresh, + gboolean error_on_add, + gboolean error_on_modify, + gboolean error_on_remove); + +#endif --- modules/smb-method.c +++ modules/smb-method.c @@ -37,6 +37,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <stdio.h> #include <gconf/gconf-client.h> #include <libgnomevfs/gnome-vfs.h> @@ -90,6 +91,17 @@ static GHashTable *user_cache = NULL; +/* smbcctx->readdir() can give us a struct smbc_dirent that identifies a + * printer, but you cannot perform a smbcctx->stat() on a path to know if that + * path refers to a printer. So, when doing readdir(), we must remember the + * URIs of the printers we have found. Later in our own do_get_file_info(), we + * will match the passed URI to this hash table and return a magic "it is a + * printer" datum if appropriate. + */ +static GHashTable *printer_hash = NULL; + +static FILE *debug_file; + #define SMB_BLOCK_SIZE (32*1024) /* Reap unused server connections and user cache after 30 minutes */ @@ -149,32 +161,105 @@ static void init_authentication (SmbAuthContext *actx, GnomeVFSURI *uri); static int perform_authentication (SmbAuthContext *actx); +static gboolean is_printer (const char *uri); + static SmbAuthContext *current_auth_context = NULL; static void auth_callback (const char *server_name, const char *share_name, char *domain, int domainmaxlen, char *username, int unmaxlen, char *password, int pwmaxlen); + +static void debug_print (const char *format, ...); +static void debug_indent (int amount); +static int debug_indentation; -#if 0 +#if 1 #define DEBUG_SMB_ENABLE #define DEBUG_SMB_LOCKS #endif #ifdef DEBUG_SMB_ENABLE -#define DEBUG_SMB(x) g_print x +#define DEBUG_SMB(x) debug_print x +#define DEBUG_IN() debug_print ("%s() {\n", G_STRFUNC); debug_indent(1) +#define DEBUG_OUT() debug_indent(-1); debug_print ("} %s()\n", G_STRFUNC) #else -#define DEBUG_SMB(x) +#define DEBUG_SMB(x) +#define DEBUG_IN() +#define DEBUG_OUT() #endif #ifdef DEBUG_SMB_LOCKS -#define LOCK_SMB() {g_mutex_lock (smb_lock); g_print ("LOCK %s\n", G_GNUC_PRETTY_FUNCTION);} -#define UNLOCK_SMB() {g_print ("UNLOCK %s\n", G_GNUC_PRETTY_FUNCTION); g_mutex_unlock (smb_lock);} +#define LOCK_SMB() {g_mutex_lock (smb_lock); debug_print ("LOCK %s\n", G_STRFUNC);} +#define UNLOCK_SMB() {debug_print ("UNLOCK %s\n", G_STRFUNC); g_mutex_unlock (smb_lock);} #else #define LOCK_SMB() g_mutex_lock (smb_lock) #define UNLOCK_SMB() g_mutex_unlock (smb_lock) #endif +static void +debug_print (const char *format, ...) +{ + va_list args; + char *str; + int i; + + if (!debug_file) + return; + + va_start (args, format); + str = g_strdup_vprintf (format, args); + va_end (args); + + fprintf (debug_file, "%p: ", g_thread_self ()); + + for (i = 0; i < debug_indentation * 4; i++) + fputc (' ', debug_file); + + fputs (str, debug_file); + g_free (str); + fflush (debug_file); +} + +static void +debug_indent (int amount) +{ + debug_indentation += amount; + if (debug_indentation < 0) + g_error ("You fucked up your indentation"); +} + +static void +DEBUG_DUMP_AUTH_CONTEXT (SmbAuthContext *actx) +{ + char *str_uri; + + if (actx->uri) + str_uri = gnome_vfs_uri_to_string (actx->uri, 0); + else + str_uri = g_strdup ("(null)"); + + debug_print ("AUTH CONTEXT %p {\n", actx); + debug_print (" uri: %s\n", str_uri); + debug_print (" vfs_result: %d\n", (int) actx->res); + debug_print (" passes: %d\n", actx->passes); + debug_print (" state: %x\n", actx->state); + debug_print (" save_auth: %d\n", actx->save_auth); + debug_print (" keyring: %s\n", actx->keyring); + debug_print (" auth_called: %d\n", actx->auth_called); + debug_print (" for_server: %s\n", actx->for_server); + debug_print (" for_share: %s\n", actx->for_share); + debug_print (" use_user: %s\n", actx->use_user); + debug_print (" use_domain: %s\n", actx->use_domain); + debug_print (" use_password: %s\n", actx->use_password); + debug_print (" cache_added: %d\n", actx->cache_added); + debug_print (" cache_used: %d\n", actx->cache_used); + debug_print (" prompt_flags: %x\n", actx->prompt_flags); + debug_print ("}\n"); + + g_free (str_uri); +} + static gchar* string_dup_nzero (const gchar *s) { @@ -319,7 +404,8 @@ * sure when we'll be called */ if (!g_mutex_trylock (smb_lock)) return TRUE; - DEBUG_SMB(("LOCK %s\n", G_GNUC_PRETTY_FUNCTION)); + DEBUG_IN (); + DEBUG_SMB(("LOCK %s\n", G_STRFUNC)); size = g_hash_table_size (server_cache); servers = g_ptr_array_sized_new (size); @@ -342,6 +428,7 @@ if (!ret) cache_reap_timeout = 0; + DEBUG_OUT (); UNLOCK_SMB(); return ret; @@ -363,6 +450,7 @@ { SmbServerCacheEntry *entry = NULL; + DEBUG_IN (); DEBUG_SMB(("[auth] adding cached server: server: %s, share: %s, domain: %s, user: %s\n", server_name ? server_name : "", share_name ? share_name : "", @@ -383,6 +471,8 @@ g_hash_table_insert (server_cache, entry, entry); current_auth_context->cache_added = TRUE; + + DEBUG_OUT (); return 0; } @@ -393,6 +483,7 @@ SmbServerCacheEntry entry; SmbServerCacheEntry *res; + DEBUG_IN (); DEBUG_SMB(("find_cached_server: server: %s, share: %s, domain: %s, user: %s\n", server_name ? server_name : "", share_name ? share_name : "", @@ -409,9 +500,13 @@ if (res != NULL) { res->last_time = time (NULL); + DEBUG_OUT (); + DEBUG_SMB (("found server %p\n", res->server)); return res->server; - } + } + DEBUG_SMB (("found nothing; returning NULL\n")); + DEBUG_OUT (); return NULL; } @@ -422,6 +517,8 @@ { SMBCSRV *srv; + DEBUG_IN (); + srv = find_cached_server (server_name, share_name, domain, username); if (srv != NULL) { DEBUG_SMB(("got cached server: server: %s, share: %s, domain: %s, user: %s\n", @@ -430,8 +527,10 @@ domain ? domain : "", username ? username : "")); current_auth_context->cache_used = TRUE; + DEBUG_OUT (); return srv; } + DEBUG_OUT (); return NULL; } @@ -454,9 +553,13 @@ static int remove_cached_server(SMBCCTX * context, SMBCSRV * server) { int removed; + + DEBUG_IN (); removed = g_hash_table_foreach_remove (server_cache, remove_server, server); + DEBUG_OUT (); + /* return 1 if failed */ return removed == 0; } @@ -483,6 +586,8 @@ gboolean could_not_purge_all; int i; + DEBUG_IN (); + size = g_hash_table_size (server_cache); servers = g_ptr_array_sized_new (size); @@ -499,6 +604,10 @@ } g_ptr_array_free (servers, TRUE); + + DEBUG_SMB (("returning could_not_purge_all = %d\n", could_not_purge_all)); + + DEBUG_OUT (); return could_not_purge_all; } @@ -519,6 +628,8 @@ SMBCFILE *dir = NULL; time_t t; struct smbc_dirent *dirent; + + DEBUG_IN (); t = time (NULL); @@ -526,6 +637,7 @@ workgroups_timestamp < t && t < workgroups_timestamp + WORKGROUP_CACHE_TIMEOUT) { /* Up to date */ + DEBUG_OUT (); return; } workgroups_timestamp = t; @@ -537,15 +649,20 @@ LOCK_SMB(); init_authentication (&actx, NULL); + DEBUG_DUMP_AUTH_CONTEXT (&actx); /* Important: perform_authentication leaves and re-enters the lock! */ while (perform_authentication (&actx) > 0) { + DEBUG_SMB (("calling ctx->opendir (\"smb://\")\n")); dir = smb_context->opendir (smb_context, "smb://"); actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB (("it returned %d\n", (int) actx.res)); } if (dir != NULL) { + DEBUG_SMB (("calling ctx->readdir() in a loop\n")); while ((dirent = smb_context->readdir (smb_context, dir)) != NULL) { + DEBUG_SMB (("got dirent '%s' of type %d\n", dirent->name, dirent->smbc_type)); if (dirent->smbc_type == SMBC_WORKGROUP && dirent->name != NULL && strlen (dirent->name) > 0) { @@ -557,9 +674,13 @@ } } + DEBUG_SMB (("calling ctx->closedir()\n")); smb_context->closedir (smb_context, dir); } + DEBUG_DUMP_AUTH_CONTEXT (&actx); UNLOCK_SMB(); + + DEBUG_OUT (); } static SmbUriType @@ -567,6 +688,9 @@ { GnomeVFSToplevelURI *toplevel; char *first_slash; + SmbUriType type; + + DEBUG_IN (); toplevel = (GnomeVFSToplevelURI *)uri; @@ -575,12 +699,16 @@ if (uri->text == NULL || uri->text[0] == 0 || strcmp (uri->text, "/") == 0) { - return SMB_URI_WHOLE_NETWORK; + type = SMB_URI_WHOLE_NETWORK; + goto out; } if (strchr (uri->text + 1, '/')) { - return SMB_URI_ERROR; + type = SMB_URI_ERROR; + goto out; } - return SMB_URI_WORKGROUP_LINK; + + type = SMB_URI_WORKGROUP_LINK; + goto out; } if (uri->text == NULL || uri->text[0] == 0 || @@ -590,9 +718,11 @@ if (!g_ascii_strcasecmp(toplevel->host_name, DEFAULT_WORKGROUP_NAME) || g_hash_table_lookup (workgroups, toplevel->host_name)) { - return SMB_URI_WORKGROUP; + type = SMB_URI_WORKGROUP; + goto out; } else { - return SMB_URI_SERVER; + type = SMB_URI_SERVER; + goto out; } } first_slash = strchr (uri->text + 1, '/'); @@ -602,13 +732,21 @@ if (!g_ascii_strcasecmp(toplevel->host_name, DEFAULT_WORKGROUP_NAME) || g_hash_table_lookup (workgroups, toplevel->host_name)) { - return SMB_URI_SERVER_LINK; + type = SMB_URI_SERVER_LINK; + goto out; } else { - return SMB_URI_SHARE; + type = SMB_URI_SHARE; + goto out; } } - - return SMB_URI_SHARE_FILE; + + type = SMB_URI_SHARE_FILE; + + out: + + DEBUG_OUT (); + + return type; } @@ -665,42 +803,56 @@ g_free (path); smb_context = smbc_new_context (); - if (smb_context != NULL) { - smb_context->debug = 0; - smb_context->callbacks.auth_fn = auth_callback; - smb_context->callbacks.add_cached_srv_fn = add_cached_server; - smb_context->callbacks.get_cached_srv_fn = get_cached_server; - smb_context->callbacks.remove_cached_srv_fn = remove_cached_server; - smb_context->callbacks.purge_cached_fn = purge_cached; - - gclient = gconf_client_get_default (); - if (gclient) { - workgroup = gconf_client_get_string (gclient, - PATH_GCONF_GNOME_VFS_SMB_WORKGROUP, NULL); - - /* libsmbclient frees this on it's own, so make sure - * to use simple system malloc */ - if (workgroup && workgroup[0]) - smb_context->workgroup = strdup (workgroup); - - g_free (workgroup); - g_object_unref (gclient); - } + if (smb_context == NULL) + goto out; - if (!smbc_init_context (smb_context)) { - smbc_free_context (smb_context, FALSE); - smb_context = NULL; - } + smb_context->debug = 0; + smb_context->callbacks.auth_fn = auth_callback; + smb_context->callbacks.add_cached_srv_fn = add_cached_server; + smb_context->callbacks.get_cached_srv_fn = get_cached_server; + smb_context->callbacks.remove_cached_srv_fn = remove_cached_server; + smb_context->callbacks.purge_cached_fn = purge_cached; + + DEBUG_SMB (("created the SMBCCTX; it has smbcctx->workgroup=\"%s\"\n", + smb_context->workgroup ? smb_context->workgroup : "(null)")); + + gclient = gconf_client_get_default (); + if (gclient) { + workgroup = gconf_client_get_string (gclient, + PATH_GCONF_GNOME_VFS_SMB_WORKGROUP, NULL); + + /* libsmbclient frees this on it's own, so make sure + * to use simple system malloc */ + if (workgroup && workgroup[0]) + smb_context->workgroup = strdup (workgroup); + + g_free (workgroup); + g_object_unref (gclient); + } + + DEBUG_SMB (("after reading from gconf, we have smbcctx->workgroup=\"%s\"\n", + smb_context->workgroup ? smb_context->workgroup : "(null)")); + if (!smbc_init_context (smb_context)) { + smbc_free_context (smb_context, FALSE); + smb_context = NULL; + DEBUG_SMB (("smbc_init_context() failed!\n")); + goto out; + } + + DEBUG_SMB (("called smbc_init_context(); we have smbcctx->workgroup=\"%s\"\n", + smb_context->workgroup ? smb_context->workgroup : "(null)")); + #if defined(HAVE_SAMBA_FLAGS) #if defined(SMB_CTX_FLAG_USE_KERBEROS) && defined(SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) - smb_context->flags |= SMB_CTX_FLAG_USE_KERBEROS | SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS; + smb_context->flags |= SMB_CTX_FLAG_USE_KERBEROS | SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS; #endif -#if defined(SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) - smb_context->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON; +# if 0 +# if defined(SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) + smb_context->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON; +# endif +# endif #endif -#endif - } server_cache = g_hash_table_new_full (server_hash, server_equal, (GDestroyNotify)server_free, NULL); @@ -708,7 +860,10 @@ g_free, NULL); user_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)user_free); - + printer_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + out: UNLOCK_SMB(); if (smb_context == NULL) { @@ -749,6 +904,8 @@ { SmbCachedUser *user; gchar *key; + + DEBUG_IN (); g_return_if_fail (actx->for_server != NULL); @@ -772,6 +929,8 @@ user->username = string_realloc (user->username, actx->use_user); user->password = string_realloc (user->password, actx->use_password); user->stamp = time (NULL); + + DEBUG_OUT (); } static gboolean @@ -779,9 +938,13 @@ { SmbCachedUser *user; gchar *key; - + gboolean retval; + g_return_val_if_fail (actx->for_server != NULL, FALSE); + DEBUG_IN (); + DEBUG_DUMP_AUTH_CONTEXT (actx); + key = g_strdup_printf ("%s/%s", actx->for_server, with_share ? actx->for_share : ""); user = (SmbCachedUser*)g_hash_table_lookup (user_cache, key); g_free (key); @@ -789,11 +952,16 @@ if (user) { /* If we already have a user name or domain double check that... */ if (!(actx->prompt_flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) && - !string_compare(user->username, actx->use_user)) - return FALSE; + !string_compare(user->username, actx->use_user)) { + retval = FALSE; + goto out; + } + if (!(actx->prompt_flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN) && - !string_compare(user->domain, actx->use_domain)) - return FALSE; + !string_compare(user->domain, actx->use_domain)) { + retval = FALSE; + goto out; + } actx->use_user = string_realloc (actx->use_user, user->username); actx->use_domain = string_realloc (actx->use_domain, user->domain); @@ -802,10 +970,16 @@ actx->use_user ? actx->use_user : "", actx->use_domain ? actx->use_domain : "", actx->use_password ? actx->use_password : "")); - return TRUE; + retval = TRUE; + goto out; } - return FALSE; + retval = FALSE; + + out: + DEBUG_DUMP_AUTH_CONTEXT (actx); + DEBUG_OUT (); + return retval; } static gboolean @@ -819,7 +993,10 @@ gboolean found_user = FALSE; char *tmp; + DEBUG_IN (); + DEBUG_SMB(("[auth] Initial authentication lookups\n")); + DEBUG_DUMP_AUTH_CONTEXT (actx); toplevel_uri = (GnomeVFSToplevelURI*)actx->uri; actx->prompt_flags = GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME | @@ -888,6 +1065,8 @@ } } + DEBUG_OUT (); + return found_user; } @@ -902,7 +1081,10 @@ gboolean filled = FALSE; g_return_val_if_fail (actx != NULL, FALSE); - g_return_val_if_fail (actx->for_server != NULL, FALSE); + g_return_val_if_fail (actx->for_server != NULL, FALSE); + + DEBUG_IN (); + DEBUG_DUMP_AUTH_CONTEXT (actx); memset (&in_args, 0, sizeof (in_args)); in_args.uri = get_auth_display_uri (actx, FALSE); @@ -923,6 +1105,8 @@ &in_args, sizeof (in_args), &out_args, sizeof (out_args)); + DEBUG_SMB(("[auth] vfs module callback for FILL_AUTHENTICATION returned invoked=%d\n", invoked)); + g_free (in_args.uri); /* If that didn't work then try without the share name */ @@ -949,7 +1133,8 @@ invoked = gnome_vfs_module_callback_invoke (GNOME_VFS_MODULE_CALLBACK_FILL_AUTHENTICATION, &in_args, sizeof (in_args), - &out_args, sizeof (out_args)); + &out_args, sizeof (out_args)); + DEBUG_SMB(("[auth] vfs module callback for FILL_AUTHENTICATION returned invoked=%d\n", invoked)); } if (invoked && out_args.valid) { @@ -975,6 +1160,9 @@ g_free (out_args.domain); g_free (out_args.password); + DEBUG_DUMP_AUTH_CONTEXT (actx); + DEBUG_OUT (); + return filled; } @@ -990,6 +1178,9 @@ g_return_val_if_fail (actx != NULL, FALSE); g_return_val_if_fail (actx->for_server != NULL, FALSE); + + DEBUG_IN (); + DEBUG_DUMP_AUTH_CONTEXT (actx); memset (&in_args, 0, sizeof (in_args)); @@ -1009,21 +1200,38 @@ in_args.default_user = actx->use_user; if (string_compare (in_args.default_user, GUEST_LOGIN)) in_args.default_user = NULL; - if (!in_args.default_user) - in_args.default_user = (char*)g_get_user_name (); - + if (!in_args.default_user) { + const char *unix_username; + const char *backslash; + + unix_username = g_get_user_name (); + backslash = strchr (unix_username, '\\'); + if (!backslash) + in_args.default_user = (char *) unix_username; + else + in_args.default_user = (char *) backslash + 1; + } + in_args.default_domain = actx->use_domain ? actx->use_domain : smb_context->workgroup; memset (&out_args, 0, sizeof (out_args)); - DEBUG_SMB(("[auth] Prompting credentials for: %s\n", - in_args.uri ? in_args.uri : "")); + DEBUG_SMB(("[auth] Prompting credentials for: uri=%s, server=%s, object=%s, username=%s, domain=%s, default_user=%s, default_domain=%s\n", + in_args.uri, + in_args.server, + in_args.object, + in_args.username, + in_args.domain, + in_args.default_user, + in_args.default_domain)); invoked = gnome_vfs_module_callback_invoke (GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION, &in_args, sizeof (in_args), &out_args, sizeof (out_args)); + DEBUG_SMB(("[auth] vfs module callback for FULL_AUTHENTICATION returned invoked=%d\n", invoked)); + if (invoked) { if (in_args.flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) { g_free (actx->use_user); @@ -1038,10 +1246,7 @@ g_free (actx->keyring); actx->save_auth = out_args.save_password; actx->keyring = actx->save_auth && out_args.keyring ? g_strdup (out_args.keyring) : NULL; - DEBUG_SMB(("[auth] Prompted credentials: %s@%s:%s\n", - actx->use_user ? actx->use_user : "", - actx->use_domain ? actx->use_domain : "", - actx->use_password ? actx->use_password : "")); + DEBUG_SMB(("[auth] Prompted credentials: %s@%s:%s keyring=%s\n", actx->use_user, actx->use_domain, actx->use_password, actx->keyring)); } *cancelled = out_args.abort_auth; @@ -1055,6 +1260,9 @@ g_free (in_args.uri); + DEBUG_DUMP_AUTH_CONTEXT (actx); + DEBUG_OUT (); + return invoked && !*cancelled; } @@ -1065,14 +1273,19 @@ GnomeVFSModuleCallbackSaveAuthenticationOut out_args; gboolean invoked; + DEBUG_IN (); + DEBUG_DUMP_AUTH_CONTEXT (actx); + /* Add to the user cache both with and without shares */ if (actx->for_server) { update_user_cache (actx, TRUE); update_user_cache (actx, FALSE); } - if (!actx->save_auth) + if (!actx->save_auth) { + DEBUG_OUT (); return; + } /* Save with the domain name */ memset (&in_args, 0, sizeof (in_args)); @@ -1101,6 +1314,8 @@ &in_args, sizeof (in_args), &out_args, sizeof (out_args)); + DEBUG_SMB(("[auth] vfs module callback for SAVE_AUTHENTICATION returned invoked=%d\n", invoked)); + g_free (in_args.uri); /* Save without the domain name */ @@ -1130,14 +1345,18 @@ &in_args, sizeof (in_args), &out_args, sizeof (out_args)); + DEBUG_SMB(("[auth] vfs module callback for SAVE_AUTHENTICATION returned invoked=%d\n", invoked)); + g_free (in_args.uri); + + DEBUG_OUT (); } static void cleanup_authentication (SmbAuthContext *actx) { /* IMPORTANT: We are IN the lock at this point */ - + DEBUG_IN (); DEBUG_SMB(("[auth] Cleaning up Authentication\n")); g_return_if_fail (actx != NULL); @@ -1161,6 +1380,9 @@ g_return_if_fail (current_auth_context == actx); current_auth_context = NULL; + + DEBUG_DUMP_AUTH_CONTEXT (actx); + DEBUG_OUT (); } /* @@ -1205,9 +1427,12 @@ { gboolean cont, auth_failed = FALSE, auth_cancelled = FALSE; int ret = -1; + + DEBUG_IN (); /* IMPORTANT: We are IN the lock at this point */ DEBUG_SMB(("[auth] perform_authentication called.\n")); + DEBUG_DUMP_AUTH_CONTEXT (actx); switch (actx->res) { case GNOME_VFS_OK: @@ -1223,8 +1448,9 @@ /* Other errors mean we're done */ default: - DEBUG_SMB(("[auth] Non-authentication error. Leaving auth loop.\n")); + DEBUG_SMB(("[auth] Non-authentication VFS error %d. Leaving auth loop.\n", (int) actx->res)); cleanup_authentication (actx); + DEBUG_OUT (); return -1; } @@ -1265,7 +1491,7 @@ /* A failed authentication */ } else if (actx->auth_called) { - + /* We need a server to perform any authentication */ g_return_val_if_fail (actx->for_server != NULL, GNOME_VFS_ERROR_INTERNAL); @@ -1276,9 +1502,12 @@ UNLOCK_SMB(); /* Do we have gnome-keyring credentials for this? */ - if (!(actx->state & SMB_AUTH_STATE_PREFILLED)) { + if (!(actx->state & SMB_AUTH_STATE_PREFILLED)) { + DEBUG_SMB(("[auth] failed authentication; will prefill from the vfs callback\n")); actx->state |= SMB_AUTH_STATE_PREFILLED; cont = prefill_authentication (actx); + } else { + DEBUG_SMB(("[auth] failed authentication; will prompt the user for authentication\n")); } /* Then we try a guest credentials... */ @@ -1303,9 +1532,10 @@ g_return_val_if_fail (current_auth_context == NULL, GNOME_VFS_ERROR_INTERNAL); current_auth_context = actx; - if (cont) + if (cont) { + DEBUG_SMB(("[auth] prefill or prompt returned 1\n")); ret = 1; - else { + } else { ret = -1; if (auth_cancelled) { @@ -1320,12 +1550,15 @@ /* Weird, don't want authentication, but failed */ } else { + DEBUG_SMB(("[auth] don't want authentication, but failed\n")); ret = -1; } } if (ret <= 0) cleanup_authentication (actx); + + DEBUG_OUT (); return ret; /* IMPORTANT: We need to still be in the lock when returning from this func */ @@ -1341,16 +1574,25 @@ SmbAuthContext *actx; SMBCSRV *server; - DEBUG_SMB (("[auth] auth_callback called: server: %s share: %s\n", + DEBUG_IN (); + + DEBUG_SMB (("[auth] auth_callback called: server: %s share: %s domain_out: %s, username_out: %s, password_out=%s\n", server_name ? server_name : "", - share_name ? share_name : "")); + share_name ? share_name : "", + domain_out ? domain_out : "", + username_out ? username_out : "", + password_out ? password_out : "")); g_return_if_fail (current_auth_context != NULL); actx = current_auth_context; + DEBUG_DUMP_AUTH_CONTEXT (actx); + /* We never authenticate for the toplevel (enumerating workgroups) */ - if (!server_name || !server_name[0]) + if (!server_name || !server_name[0]) { + DEBUG_OUT (); return; + } actx->auth_called = TRUE; @@ -1361,8 +1603,10 @@ actx->for_share = string_dup_nzero (share_name); /* The first pass, try the cache, fill in anything we know */ - if (actx->passes == 1) + if (actx->passes == 1) { + DEBUG_SMB(("[auth] first pass; call initial_authentication()\n")); initial_authentication (actx); + } /* If we have a valid user then go for it */ if (actx->use_user) { @@ -1380,9 +1624,9 @@ g_assert (!actx->preset_user); if (actx->passes == 1) - DEBUG_SMB(("[auth] No credentials, trying anonymous user login\n")); + DEBUG_SMB(("[auth] No credentials, trying anonymous user login with empty password\n")); else - DEBUG_SMB(("[auth] No credentials, returning null values\n")); + DEBUG_SMB(("[auth] No credentials, returning empty user and password\n")); strncpy (username_out, "", unmaxlen); strncpy (password_out, "", pwmaxlen); @@ -1398,15 +1642,30 @@ * this doesn't make much sense, but such is life with libsmbclient. */ if ((actx->state & SMB_AUTH_STATE_PROMPTED) && actx->res != GNOME_VFS_OK) { + DEBUG_SMB(("[auth] we had prompted already but auth failed. Calling find_cached_server() again\n")); server = find_cached_server (server_name, share_name, domain_out, username_out); if (server) { DEBUG_SMB (("[auth] auth_callback. Remove the wrong server entry from server_cache.\n")); g_hash_table_foreach_remove (server_cache, remove_server, server); } } + + DEBUG_OUT (); } static char * +get_printer_data (const char *display_name, const char *path) +{ + return g_strdup_printf ("[Desktop Entry]\n" + "Encoding=UTF-8\n" + "Name=%s\n" + "Type=Application\n" + "Exec=gnome-cups-add --printer=%s\n" + "Icon=printer-remote\n", /* per the freedesktop.org icon naming spec */ + display_name, path); +} + +static char * get_workgroup_data (const char *display_name, const char *name) { return g_strdup_printf ("[Desktop Entry]\n" @@ -1453,6 +1712,21 @@ GnomeVFSFileOffset file_size; } FileHandle; +/* Takes ownership of desktop_file_contents */ +static FileHandle * +file_handle_new_from_desktop_file_contents (char *desktop_file_contents) +{ + FileHandle *handle; + + handle = g_new (FileHandle, 1); + handle->is_data = TRUE; + handle->offset = 0; + handle->file_data = desktop_file_contents; + handle->file_size = strlen (handle->file_data); + + return handle; +} + static GnomeVFSResult do_open (GnomeVFSMethod *method, GnomeVFSMethodHandle **method_handle, @@ -1466,59 +1740,84 @@ int type; mode_t unix_mode; SMBCFILE *file = NULL; - + GnomeVFSResult result; + + DEBUG_IN (); DEBUG_SMB(("do_open() %s mode %d\n", gnome_vfs_uri_to_string (uri, 0), mode)); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } + + path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD); + + if (is_printer (path)) { + if (mode & GNOME_VFS_OPEN_WRITE) { + result = GNOME_VFS_ERROR_READ_ONLY; + goto out; + } + + unescaped_name = get_base_from_uri (uri); + name = gnome_vfs_uri_extract_short_path_name (uri); + + handle = file_handle_new_from_desktop_file_contents (get_printer_data (unescaped_name, path)); + *method_handle = (GnomeVFSMethodHandle *)handle; + + g_free (unescaped_name); + g_free (name); + + result = GNOME_VFS_OK; + goto out; + } if (type == SMB_URI_WHOLE_NETWORK || type == SMB_URI_WORKGROUP || type == SMB_URI_SERVER || type == SMB_URI_SHARE) { - return GNOME_VFS_ERROR_IS_DIRECTORY; + result = GNOME_VFS_ERROR_IS_DIRECTORY; + goto out; } if (type == SMB_URI_WORKGROUP_LINK) { if (mode & GNOME_VFS_OPEN_WRITE) { - return GNOME_VFS_ERROR_READ_ONLY; + result = GNOME_VFS_ERROR_READ_ONLY; + goto out; } - handle = g_new (FileHandle, 1); - handle->is_data = TRUE; - handle->offset = 0; + unescaped_name = get_base_from_uri (uri); name = gnome_vfs_uri_extract_short_path_name (uri); - handle->file_data = get_workgroup_data (unescaped_name, name); - handle->file_size = strlen (handle->file_data); + + handle = file_handle_new_from_desktop_file_contents (get_workgroup_data (unescaped_name, name)); + *method_handle = (GnomeVFSMethodHandle *)handle; + g_free (unescaped_name); g_free (name); - *method_handle = (GnomeVFSMethodHandle *)handle; - - return GNOME_VFS_OK; + result = GNOME_VFS_OK; + goto out; } if (type == SMB_URI_SERVER_LINK) { if (mode & GNOME_VFS_OPEN_WRITE) { - return GNOME_VFS_ERROR_READ_ONLY; + result = GNOME_VFS_ERROR_READ_ONLY; + goto out; } - handle = g_new (FileHandle, 1); - handle->is_data = TRUE; - handle->offset = 0; + unescaped_name = get_base_from_uri (uri); name = gnome_vfs_uri_extract_short_path_name (uri); - handle->file_data = get_computer_data (unescaped_name, name); - handle->file_size = strlen (handle->file_data); + + handle = file_handle_new_from_desktop_file_contents (get_computer_data (unescaped_name, name)); + *method_handle = (GnomeVFSMethodHandle *)handle; + g_free (unescaped_name); g_free (name); - *method_handle = (GnomeVFSMethodHandle *)handle; - - return GNOME_VFS_OK; + result = GNOME_VFS_OK; + goto out; } g_assert (type == SMB_URI_SHARE_FILE); @@ -1531,16 +1830,16 @@ } else { if (mode & GNOME_VFS_OPEN_WRITE) unix_mode = O_WRONLY; - else - return GNOME_VFS_ERROR_INVALID_OPEN_MODE; + else { + result = GNOME_VFS_ERROR_INVALID_OPEN_MODE; + goto out; + } } if ((mode & GNOME_VFS_OPEN_TRUNCATE) || (!(mode & GNOME_VFS_OPEN_RANDOM) && (mode & GNOME_VFS_OPEN_WRITE))) unix_mode |= O_TRUNC; - path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD); - LOCK_SMB(); init_authentication (&actx, uri); @@ -1548,14 +1847,15 @@ while (perform_authentication (&actx) > 0) { file = smb_context->open (smb_context, path, unix_mode, 0666); actx.res = (file != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->open(\"%s\") returned file %p and error %d\n", path, file, (int) actx.res)); } UNLOCK_SMB(); - g_free (path); - - if (file == NULL) + if (file == NULL) { + DEBUG_OUT (); return actx.res; + } handle = g_new (FileHandle, 1); handle->is_data = FALSE; @@ -1563,7 +1863,14 @@ *method_handle = (GnomeVFSMethodHandle *)handle; - return GNOME_VFS_OK; + result = GNOME_VFS_OK; + + out: + + g_free (path); + + DEBUG_OUT (); + return result; } static GnomeVFSResult @@ -1577,6 +1884,7 @@ GnomeVFSResult res; int r; + DEBUG_IN (); DEBUG_SMB(("do_close()\n")); res = GNOME_VFS_OK; @@ -1595,6 +1903,7 @@ r = smb_context->close_fn (smb_context, handle->file); #endif actx.res = (r >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->close(%p) returned error %d\n", handle->file, (int) actx.res)); } res = actx.res; @@ -1602,6 +1911,7 @@ } g_free (handle); + DEBUG_OUT (); return res; } @@ -1618,6 +1928,7 @@ SmbAuthContext actx; ssize_t n = 0; + DEBUG_IN (); DEBUG_SMB(("do_read() %Lu bytes\n", num_bytes)); if (handle->is_data) { @@ -1643,11 +1954,14 @@ *bytes_read = (n < 0) ? 0 : n; - if (n == 0) + if (n == 0) { + DEBUG_OUT (); return GNOME_VFS_ERROR_EOF; + } handle->offset += n; + DEBUG_OUT (); return res; } @@ -1665,10 +1979,13 @@ SmbAuthContext actx; ssize_t written = 0; + DEBUG_IN (); DEBUG_SMB (("do_write() %p\n", method_handle)); - if (handle->is_data) + if (handle->is_data) { + DEBUG_OUT (); return GNOME_VFS_ERROR_READ_ONLY; + } LOCK_SMB(); init_authentication (&actx, NULL); @@ -1682,6 +1999,7 @@ UNLOCK_SMB(); *bytes_written = (written < 0) ? 0 : written; + DEBUG_OUT (); return actx.res; } @@ -1700,30 +2018,39 @@ SMBCFILE *file = NULL; FileHandle *handle; SmbAuthContext actx; - + + DEBUG_IN (); DEBUG_SMB (("do_create() %s mode %d\n", gnome_vfs_uri_to_string (uri, 0), mode)); type = smb_uri_type (uri); - if (type == SMB_URI_ERROR) + if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; + } if (type == SMB_URI_WHOLE_NETWORK || type == SMB_URI_WORKGROUP || type == SMB_URI_SERVER || - type == SMB_URI_SHARE) + type == SMB_URI_SHARE) { + DEBUG_OUT (); return GNOME_VFS_ERROR_IS_DIRECTORY; + } if (type == SMB_URI_WORKGROUP_LINK || - type == SMB_URI_SERVER_LINK) + type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; + } unix_mode = O_CREAT | O_TRUNC; - if (!(mode & GNOME_VFS_OPEN_WRITE)) + if (!(mode & GNOME_VFS_OPEN_WRITE)) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_OPEN_MODE; + } if (mode & GNOME_VFS_OPEN_READ) unix_mode |= O_RDWR; @@ -1742,24 +2069,44 @@ while (perform_authentication (&actx) > 0) { file = smb_context->open (smb_context, path, unix_mode, perm); actx.res = (file != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->open(\"%s\") returned file %p and error %d\n", path, file, (int) actx.res)); } UNLOCK_SMB(); g_free (path); - if (file == NULL) + if (file == NULL) { + DEBUG_OUT (); return actx.res; + } handle = g_new (FileHandle, 1); handle->is_data = FALSE; handle->file = file; *method_handle = (GnomeVFSMethodHandle *)handle; - + + DEBUG_OUT (); return GNOME_VFS_OK; } +static void +set_file_info_to_readonly_desktop_file (GnomeVFSFileInfo *file_info, GnomeVFSURI *uri) +{ + file_info->name = get_base_from_uri (uri); + file_info->valid_fields = file_info->valid_fields + | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE + | GNOME_VFS_FILE_INFO_FIELDS_TYPE + | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS; + file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; + file_info->mime_type = g_strdup ("application/x-desktop"); + file_info->permissions = + GNOME_VFS_PERM_USER_READ | + GNOME_VFS_PERM_OTHER_READ | + GNOME_VFS_PERM_GROUP_READ; +} + static GnomeVFSResult do_get_file_info (GnomeVFSMethod *method, GnomeVFSURI *uri, @@ -1774,19 +2121,34 @@ const char *mime_type; SmbAuthContext actx; + DEBUG_IN (); DEBUG_SMB (("do_get_file_info() %s\n", gnome_vfs_uri_to_string (uri, 0))); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } + + path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD); + + if (is_printer (path)) { + DEBUG_SMB (("is a printer we already saw; will fill in info\n")); + + set_file_info_to_readonly_desktop_file (file_info, uri); + g_free (path); + + DEBUG_OUT (); + return GNOME_VFS_OK; + } if (type == SMB_URI_WHOLE_NETWORK || type == SMB_URI_WORKGROUP || type == SMB_URI_SERVER || type == SMB_URI_SHARE) { + DEBUG_SMB (("is whole network, workgroup, server, or share\n")); file_info->name = get_base_from_uri (uri); file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; @@ -1807,29 +2169,22 @@ GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_GROUP_READ; } + DEBUG_OUT(); return GNOME_VFS_OK; } if (type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { - file_info->name = get_base_from_uri (uri); - file_info->valid_fields = file_info->valid_fields - | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE - | GNOME_VFS_FILE_INFO_FIELDS_TYPE - | GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS; - file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; - file_info->mime_type = g_strdup ("application/x-desktop"); - file_info->permissions = - GNOME_VFS_PERM_USER_READ | - GNOME_VFS_PERM_OTHER_READ | - GNOME_VFS_PERM_GROUP_READ; + DEBUG_SMB (("is workgroup link, or server link\n")); + set_file_info_to_readonly_desktop_file (file_info, uri); + DEBUG_OUT (); return GNOME_VFS_OK; } g_assert (type == SMB_URI_SHARE_FILE); - - path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD); + DEBUG_SMB (("is share file\n")); + LOCK_SMB(); init_authentication (&actx, uri); @@ -1837,14 +2192,17 @@ while (perform_authentication (&actx) > 0) { err = smb_context->stat (smb_context, path, &st); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->stat(\"%s\") returned error %d\n", path, (int) actx.res)); } UNLOCK_SMB(); g_free (path); - if (err < 0) + if (err < 0) { + DEBUG_OUT (); return actx.res; + } gnome_vfs_stat_to_file_info (file_info, &st); file_info->name = get_base_from_uri (uri); @@ -1870,6 +2228,7 @@ file_info->name, type, file_info->mime_type, file_info->type)); + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -1885,6 +2244,7 @@ struct stat st; int err = -1; + DEBUG_IN (); LOCK_SMB(); init_authentication (&actx, NULL); @@ -1892,17 +2252,22 @@ while (perform_authentication (&actx) > 0) { err = smb_context->fstat (smb_context, handle->file, &st); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->fstat(%p) returned error %d\n", handle->file, (int) actx.res)); } UNLOCK_SMB(); - if (err < 0) + if (err < 0) { + DEBUG_OUT (); return actx.res; + } gnome_vfs_stat_to_file_info (file_info, &st); file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE; file_info->io_block_size = SMB_BLOCK_SIZE; + + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -1910,7 +2275,19 @@ do_is_local (GnomeVFSMethod *method, const GnomeVFSURI *uri) { - return FALSE; + char *path; + gboolean is_local; + + /* FIXME: This is a hack. In get_printer_data(), we generate data for a desktop item. This item + * is a "Type=Application" launcher, which launches gnome-cups-add. However, since we can't execute + * .desktop files from remote sites, we only advertise that printers "are local files". + */ + + path = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_USER_NAME | GNOME_VFS_URI_HIDE_PASSWORD); + is_local = is_printer (path); + g_free (path); + + return is_local; } typedef struct { @@ -1948,6 +2325,7 @@ SMBCFILE *dir = NULL; SmbAuthContext actx; + DEBUG_IN (); DEBUG_SMB(("do_open_directory() %s\n", gnome_vfs_uri_to_string (uri, 0))); @@ -1959,12 +2337,14 @@ directory_handle = g_new0 (DirectoryHandle, 1); g_hash_table_foreach (workgroups, add_workgroup, directory_handle); *method_handle = (GnomeVFSMethodHandle *) directory_handle; + DEBUG_OUT (); return GNOME_VFS_OK; } if (type == SMB_URI_ERROR || type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_A_DIRECTORY; } @@ -1973,11 +2353,22 @@ host_name = gnome_vfs_uri_get_host_name (uri); if (type == SMB_URI_WORKGROUP && host_name != NULL && !g_ascii_strcasecmp(host_name, DEFAULT_WORKGROUP_NAME)) { + char *new_workgroup; + new_uri = gnome_vfs_uri_dup (uri); - gnome_vfs_uri_set_host_name (new_uri, - smb_context->workgroup - ? smb_context->workgroup - : "WORKGROUP"); + if (smb_context->workgroup) + new_workgroup = smb_context->workgroup; + else + new_workgroup = "WORKGROUP"; + + DEBUG_SMB (("we are being asked for %s; substituting it for workgroup \"%s\"%s\n", + DEFAULT_WORKGROUP_NAME, + new_workgroup, + (smb_context->workgroup + ? " because that is what was in the smbcctx->workgroup" + : " because smbcctx->workgroup=NULL, so we use this as a last resort"))); + + gnome_vfs_uri_set_host_name (new_uri, new_workgroup); uri = new_uri; } @@ -1992,6 +2383,7 @@ while (perform_authentication (&actx) > 0) { dir = smb_context->opendir (smb_context, path); actx.res = (dir != NULL) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->opendir(\"%s\") returned dir %p and error %d\n", path, dir, (int) actx.res)); } UNLOCK_SMB(); @@ -2001,6 +2393,7 @@ if (dir == NULL) { g_free (path); + DEBUG_OUT (); return actx.res; } @@ -2010,6 +2403,7 @@ directory_handle->path = path; *method_handle = (GnomeVFSMethodHandle *) directory_handle; + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2024,10 +2418,13 @@ GList *l; int err = -1; + DEBUG_IN (); DEBUG_SMB(("do_close_directory: %p\n", directory_handle)); - if (directory_handle == NULL) + if (directory_handle == NULL) { + DEBUG_OUT (); return GNOME_VFS_OK; + } if (directory_handle->workgroups != NULL) { for (l = directory_handle->workgroups; l != NULL; l = l->next) { @@ -2046,6 +2443,7 @@ while (perform_authentication (&actx) > 0) { err = smb_context->closedir (smb_context, directory_handle->dir); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->closedir(%p) returned error %d\n", directory_handle->dir, (int) actx.res)); } res = actx.res; @@ -2054,9 +2452,46 @@ g_free (directory_handle->path); g_free (directory_handle); + DEBUG_OUT (); return res; } +static char * +make_path_from_uri_and_name (const char *path, const char *name) +{ + char *escaped_name; + char *new_path; + + escaped_name = gnome_vfs_escape_string (name); + + if (path[strlen(path) - 1] == '/') { + new_path = g_strconcat (path, escaped_name, NULL); + } else { + new_path = g_strconcat (path, "/", escaped_name, NULL); + } + + g_free (escaped_name); + + return new_path; +} + +static void +add_printer_to_hash (DirectoryHandle *dh, + GnomeVFSFileInfo *file_info) +{ + char *path; + + path = make_path_from_uri_and_name (dh->path, file_info->name); + + g_hash_table_insert (printer_hash, path, path); +} + +static gboolean +is_printer (const char *uri) +{ + return g_hash_table_lookup (printer_hash, uri) != NULL; +} + static GnomeVFSResult do_read_directory (GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle, @@ -2068,14 +2503,15 @@ SmbAuthContext actx; struct stat st; char *statpath; - char *path; int r = -1; GList *l; + DEBUG_IN (); DEBUG_SMB (("do_read_directory()\n")); if (dh->dir == NULL) { if (dh->workgroups == NULL) { + DEBUG_OUT (); return GNOME_VFS_ERROR_EOF; } else { /* workgroup link */ @@ -2089,6 +2525,7 @@ | GNOME_VFS_FILE_INFO_FIELDS_TYPE; file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; file_info->mime_type = g_strdup ("application/x-desktop"); + DEBUG_OUT (); return GNOME_VFS_OK; } } @@ -2112,16 +2549,20 @@ } else { actx.res = GNOME_VFS_OK; } + DEBUG_SMB(("ctx->readdir(%p) returned entry %p and error %d\n", dh->dir, entry, (int) actx.res)); } if (entry == NULL) { UNLOCK_SMB(); + DEBUG_OUT (); return actx.res; } } while (entry->smbc_type == SMBC_COMMS_SHARE || entry->smbc_type == SMBC_IPC_SHARE || +#if 0 entry->smbc_type == SMBC_PRINTER_SHARE || +#endif entry->name == NULL || strlen (entry->name) == 0 || (entry->smbc_type == SMBC_FILE_SHARE && @@ -2151,29 +2592,24 @@ file_info->mime_type = g_strdup ("application/x-desktop"); break; case SMBC_PRINTER_SHARE: +#if 0 /* Ignored above for now */ +#endif file_info->valid_fields = file_info->valid_fields | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | GNOME_VFS_FILE_INFO_FIELDS_TYPE; file_info->type = GNOME_VFS_FILE_TYPE_REGULAR; - file_info->mime_type = g_strdup ("application/x-smb-printer"); + file_info->mime_type = g_strdup ("application/x-desktop"); /* we'll generate the fake .desktop in do_open() */ + add_printer_to_hash (dh, file_info); + debug_print ("GOT PRINTER: \"%s\"", file_info->name); + break; case SMBC_COMMS_SHARE: case SMBC_IPC_SHARE: break; case SMBC_DIR: case SMBC_FILE: - path = dh->path; - - if (path[strlen(path)-1] == '/') { - statpath = g_strconcat (path, - gnome_vfs_escape_string (file_info->name), - NULL); - } else { - statpath = g_strconcat (path, - "/", - gnome_vfs_escape_string (file_info->name), - NULL); - } + statpath = make_path_from_uri_and_name (dh->path, file_info->name); + /* TODO: might give an auth error, but should be rare due to the succeeding opendir. If this happens and we can't auth, we should terminate the readdir to avoid multiple @@ -2187,6 +2623,7 @@ while (perform_authentication (&actx) > 0) { r = smb_context->stat (smb_context, statpath, &st); actx.res = (r == 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->stat(\"%s\") returned error %d\n", statpath, (int) actx.res)); } UNLOCK_SMB(); @@ -2221,6 +2658,7 @@ g_assert_not_reached (); } + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2236,6 +2674,8 @@ int meth_whence; off_t ret = (off_t) -1; + DEBUG_IN (); + if (handle->is_data) { switch (whence) { case GNOME_VFS_SEEK_START: @@ -2252,8 +2692,11 @@ } break; default: + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_SUPPORTED; } + + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2268,6 +2711,7 @@ meth_whence = SEEK_END; break; default: + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_SUPPORTED; } @@ -2280,7 +2724,8 @@ actx.res = (ret != (off_t) -1) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); } UNLOCK_SMB(); - + + DEBUG_OUT (); return actx.res; } @@ -2293,8 +2738,11 @@ SmbAuthContext actx; off_t ret = (off_t) -1; + DEBUG_IN (); + if (handle->is_data) { *offset_return = handle->offset; + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2309,6 +2757,8 @@ UNLOCK_SMB(); *offset_return = (ret == (off_t) -1) ? 0 : (GnomeVFSFileOffset) ret; + + DEBUG_OUT (); return actx.res; } @@ -2321,12 +2771,14 @@ SmbAuthContext actx; int type, err = -1; + DEBUG_IN (); DEBUG_SMB (("do_unlink() %s\n", gnome_vfs_uri_to_string (uri, 0))); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } @@ -2336,6 +2788,7 @@ type == SMB_URI_SHARE || type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; } @@ -2348,12 +2801,14 @@ while (perform_authentication (&actx) > 0) { err = smb_context->unlink (smb_context, path); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->unlink(\"%s\") returned error %d\n", path, (int) actx.res)); } UNLOCK_SMB(); g_free (path); - + + DEBUG_OUT (); return actx.res; } @@ -2370,6 +2825,7 @@ char *path2; char *p1, *p2; + DEBUG_IN (); DEBUG_SMB (("do_check_same_fs()\n")); server1 = @@ -2390,6 +2846,7 @@ g_free (path1); g_free (path2); *same_fs_return = FALSE; + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2418,6 +2875,7 @@ g_free (path1); g_free (path2); + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2434,7 +2892,7 @@ SmbAuthContext actx; int old_type, new_type; - + DEBUG_IN (); DEBUG_SMB (("do_move() %s %s\n", gnome_vfs_uri_to_string (old_uri, 0), gnome_vfs_uri_to_string (new_uri, 0))); @@ -2444,6 +2902,7 @@ if (old_type != SMB_URI_SHARE_FILE || new_type != SMB_URI_SHARE_FILE) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; } @@ -2461,6 +2920,7 @@ err = smb_context->rename (smb_context, old_path, smb_context, new_path); errnox = errno; actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->rename(\"%s\", \"%s\") returned error %d\n", old_path, new_path, (int) actx.res)); } UNLOCK_SMB(); @@ -2477,6 +2937,7 @@ while (perform_authentication (&actx) > 0) { err = smb_context->unlink (smb_context, new_path); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->unlink(\"%s\") returned error %d\n", new_path, (int) actx.res)); } UNLOCK_SMB(); @@ -2493,6 +2954,7 @@ g_free (old_path); g_free (new_path); + DEBUG_OUT (); return actx.res; } @@ -2517,9 +2979,11 @@ int type, err = -1; SmbAuthContext actx; + DEBUG_IN (); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } @@ -2529,6 +2993,7 @@ type == SMB_URI_SHARE || type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; } @@ -2542,12 +3007,14 @@ while (perform_authentication (&actx) > 0) { err = smb_context->mkdir (smb_context, path, perm); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->mkdir(\"%s\") returned error %d\n", path, (int) actx.res)); } UNLOCK_SMB(); g_free (path); + DEBUG_OUT (); return actx.res; } @@ -2560,9 +3027,11 @@ int err = -1, type; SmbAuthContext actx; + DEBUG_IN (); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } @@ -2572,6 +3041,7 @@ type == SMB_URI_SHARE || type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; } @@ -2584,11 +3054,13 @@ while (perform_authentication (&actx) > 0) { err = smb_context->rmdir (smb_context, path); actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->rmdir(\"%s\") returned error %d\n", path, (int) actx.res)); } UNLOCK_SMB(); g_free (path); + DEBUG_OUT (); return actx.res; } @@ -2603,11 +3075,13 @@ int err = -1, errnox = 0, type; SmbAuthContext actx; + DEBUG_IN (); DEBUG_SMB (("do_set_file_info: mask %x\n", mask)); type = smb_uri_type (uri); if (type == SMB_URI_ERROR) { + DEBUG_OUT (); return GNOME_VFS_ERROR_INVALID_URI; } @@ -2617,6 +3091,7 @@ type == SMB_URI_SHARE || type == SMB_URI_WORKGROUP_LINK || type == SMB_URI_SERVER_LINK) { + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_PERMITTED; } @@ -2640,6 +3115,7 @@ err = smb_context->rename (smb_context, path, smb_context, new_path); errnox = errno; actx.res = (err >= 0) ? GNOME_VFS_OK : gnome_vfs_result_from_errno (); + DEBUG_SMB(("ctx->rename(\"%s\", \"%s\") returned error %d\n", path, new_path, (int) actx.res)); } UNLOCK_SMB(); @@ -2652,31 +3128,37 @@ if (actx.res != GNOME_VFS_OK) { g_free (path); + DEBUG_OUT (); return actx.res; } } if (gnome_vfs_context_check_cancellation (context)) { g_free (path); + DEBUG_OUT (); return GNOME_VFS_ERROR_CANCELLED; } if (mask & GNOME_VFS_SET_FILE_INFO_PERMISSIONS) { g_free (path); + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_SUPPORTED; } if (mask & GNOME_VFS_SET_FILE_INFO_OWNER) { g_free (path); + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_SUPPORTED; } if (mask & GNOME_VFS_SET_FILE_INFO_TIME) { g_free (path); + DEBUG_OUT (); return GNOME_VFS_ERROR_NOT_SUPPORTED; } g_free (path); + DEBUG_OUT (); return GNOME_VFS_OK; } @@ -2708,11 +3190,50 @@ NULL /* do_create_symbolic_link */ }; +static void +debug_init (void) +{ + char *debug_flag_path; + struct stat st; + + LOCK_SMB (); + + debug_flag_path = g_build_filename (g_get_home_dir (), ".debug-gnome-vfs-smb", NULL); + + if (stat (debug_flag_path, &st) == 0) { + char *debug_filename; + + debug_filename = g_build_filename (g_get_home_dir (), "debug-gnome-vfs-smb.log", NULL); + debug_file = fopen (debug_filename, "w"); + g_free (debug_filename); + } else + debug_file = NULL; + + g_free (debug_flag_path); + + UNLOCK_SMB (); +} + +static void +debug_shutdown (void) +{ + LOCK_SMB (); + + if (debug_file) { + fclose (debug_file); + debug_file = NULL; + } + + UNLOCK_SMB (); +} + GnomeVFSMethod * vfs_module_init (const char *method_name, const char *args) { smb_lock = g_mutex_new (); + debug_init (); + DEBUG_SMB (("<-- smb module init called -->\n")); if (try_init ()) { @@ -2735,9 +3256,12 @@ g_hash_table_destroy (server_cache); g_hash_table_destroy (workgroups); g_hash_table_destroy (user_cache); + g_hash_table_destroy (printer_hash); g_mutex_free (smb_lock); DEBUG_SMB (("<-- smb module shutdown called -->\n")); + + debug_shutdown (); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor