1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 #include <config.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
35 #include <gio/gio.h>
36
37 #include "gvfsbackendsmbbrowse.h"
38 #include "gvfsjobmountmountable.h"
39 #include "gvfsjobopenforread.h"
40 #include "gvfsjobread.h"
41 #include "gvfsjobseekread.h"
42 #include "gvfsjobqueryinfo.h"
43 #include "gvfsjobenumerate.h"
44 #include "gvfsdaemonprotocol.h"
45 #include "gvfskeyring.h"
46 #include "gmounttracker.h"
47 #include "gvfsbackendsmbprivate.h"
48 #include "gvfsutils.h"
49
50 #include <libsmbclient.h>
51
52 /* The magic "default workgroup" hostname */
53 #define DEFAULT_WORKGROUP_NAME "X-GNOME-DEFAULT-WORKGROUP"
54
55 /* Time in seconds before we mark dirents cache outdated */
56 #define DEFAULT_CACHE_EXPIRATION_TIME 10
57
58 typedef struct {
59 unsigned int smbc_type;
60 char *name;
61 char *name_normalized;
62 char *name_utf8;
63 char *comment;
64 } BrowseEntry;
65
66 struct _GVfsBackendSmbBrowse
67 {
68 GVfsBackend parent_instance;
69
70 char *user;
71 char *domain;
72 char *server;
73 char *mounted_server; /* server or DEFAULT_WORKGROUP_NAME */
74 char *default_workgroup;
75 int port;
76 SMBCCTX *smb_context;
77
78 char *last_user;
79 char *last_domain;
80 char *last_password;
81
82 GMountSource *mount_source;
83 int mount_try;
84 gboolean mount_try_again;
85 gboolean mount_cancelled;
86
87 gboolean password_in_keyring;
88 GPasswordSave password_save;
89
90 GMutex entries_lock;
91 GMutex update_cache_lock;
92 time_t last_entry_update;
93 GList *entries;
94 };
95
96
97 static GHashTable *server_cache = NULL;
98 static GMountTracker *mount_tracker = NULL;
99
100 typedef struct {
101 char *server_name;
102 char *share_name;
103 char *domain;
104 char *username;
105 } CachedServer;
106
G_DEFINE_TYPE(GVfsBackendSmbBrowse,g_vfs_backend_smb_browse,G_VFS_TYPE_BACKEND)107 G_DEFINE_TYPE (GVfsBackendSmbBrowse, g_vfs_backend_smb_browse, G_VFS_TYPE_BACKEND)
108
109 static char *
110 normalize_smb_name_helper (const char *name, gssize len, gboolean valid_utf8)
111 {
112 if (valid_utf8)
113 return g_utf8_casefold (name, len);
114 else
115 return g_ascii_strdown (name, len);
116 }
117
118 static char *
normalize_smb_name(const char * name,gssize len)119 normalize_smb_name (const char *name, gssize len)
120 {
121 gboolean valid_utf8;
122
123 valid_utf8 = g_utf8_validate (name, len, NULL);
124 return normalize_smb_name_helper (name, len, valid_utf8);
125 }
126
127 static char *
smb_name_to_utf8(const char * name,gboolean * valid_utf8_out)128 smb_name_to_utf8 (const char *name, gboolean *valid_utf8_out)
129 {
130 GString *string;
131 const gchar *remainder, *invalid;
132 gint remaining_bytes, valid_bytes;
133 gboolean valid_utf8;
134
135 remainder = name;
136 remaining_bytes = strlen (name);
137 valid_utf8 = TRUE;
138
139 string = g_string_sized_new (remaining_bytes);
140 while (remaining_bytes != 0)
141 {
142 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
143 break;
144 valid_utf8 = FALSE;
145
146 valid_bytes = invalid - remainder;
147
148 g_string_append_len (string, remainder, valid_bytes);
149 /* append U+FFFD REPLACEMENT CHARACTER */
150 g_string_append (string, "\357\277\275");
151
152 remaining_bytes -= valid_bytes + 1;
153 remainder = invalid + 1;
154 }
155
156 g_string_append (string, remainder);
157
158 if (valid_utf8_out)
159 *valid_utf8_out = valid_utf8;
160
161 return g_string_free (string, FALSE);
162 }
163
164 static void
browse_entry_free(BrowseEntry * entry)165 browse_entry_free (BrowseEntry *entry)
166 {
167 g_free (entry->name);
168 g_free (entry->comment);
169 g_free (entry);
170 }
171
172 static gboolean
cached_server_equal(gconstpointer _a,gconstpointer _b)173 cached_server_equal (gconstpointer _a,
174 gconstpointer _b)
175 {
176 const CachedServer *a = _a;
177 const CachedServer *b = _b;
178
179 return
180 strcmp (a->server_name, b->server_name) == 0 &&
181 strcmp (a->share_name, b->share_name) == 0 &&
182 strcmp (a->domain, b->domain) == 0 &&
183 strcmp (a->username, b->username) == 0;
184 }
185
186 static guint
cached_server_hash(gconstpointer key)187 cached_server_hash (gconstpointer key)
188 {
189 const CachedServer *server = key;
190
191 return
192 g_str_hash (server->server_name) ^
193 g_str_hash (server->share_name) ^
194 g_str_hash (server->domain) ^
195 g_str_hash (server->username);
196 }
197
198 static void
cached_server_free(CachedServer * server)199 cached_server_free (CachedServer *server)
200 {
201 g_free (server->server_name);
202 g_free (server->share_name);
203 g_free (server->domain);
204 g_free (server->username);
205 g_free (server);
206 }
207
208 static void
g_vfs_backend_smb_browse_finalize(GObject * object)209 g_vfs_backend_smb_browse_finalize (GObject *object)
210 {
211 GVfsBackendSmbBrowse *backend;
212
213 backend = G_VFS_BACKEND_SMB_BROWSE (object);
214
215 g_free (backend->user);
216 g_free (backend->domain);
217 g_free (backend->mounted_server);
218 g_free (backend->server);
219 g_free (backend->default_workgroup);
220
221 g_mutex_clear (&backend->entries_lock);
222 g_mutex_clear (&backend->update_cache_lock);
223
224 smbc_free_context (backend->smb_context, TRUE);
225
226 g_list_free_full (backend->entries, (GDestroyNotify)browse_entry_free);
227
228 if (G_OBJECT_CLASS (g_vfs_backend_smb_browse_parent_class)->finalize)
229 (*G_OBJECT_CLASS (g_vfs_backend_smb_browse_parent_class)->finalize) (object);
230 }
231
232 static void
g_vfs_backend_smb_browse_init(GVfsBackendSmbBrowse * backend)233 g_vfs_backend_smb_browse_init (GVfsBackendSmbBrowse *backend)
234 {
235 char *workgroup;
236 GSettings *settings;
237
238 g_mutex_init (&backend->entries_lock);
239 g_mutex_init (&backend->update_cache_lock);
240
241 if (mount_tracker == NULL)
242 mount_tracker = g_mount_tracker_new (NULL, FALSE);
243
244 /* Get default workgroup name */
245 settings = g_settings_new ("org.gnome.system.smb");
246
247 workgroup = g_settings_get_string (settings, "workgroup");
248 if (workgroup && workgroup[0])
249 backend->default_workgroup = workgroup;
250 else
251 g_free (workgroup);
252
253 g_object_unref (settings);
254
255 g_debug ("g_vfs_backend_smb_browse_init: default workgroup = '%s'\n", backend->default_workgroup ? backend->default_workgroup : "NULL");
256 }
257
258 /**
259 * Authentication callback function type (method that includes context)
260 *
261 * Type for the the authentication function called by the library to
262 * obtain authentication credentals
263 *
264 * @param context Pointer to the smb context
265 * @param srv Server being authenticated to
266 * @param shr Share being authenticated to
267 * @param wg Pointer to buffer containing a "hint" for the
268 * workgroup to be authenticated. Should be filled in
269 * with the correct workgroup if the hint is wrong.
270 * @param wglen The size of the workgroup buffer in bytes
271 * @param un Pointer to buffer containing a "hint" for the
272 * user name to be use for authentication. Should be
273 * filled in with the correct workgroup if the hint is
274 * wrong.
275 * @param unlen The size of the username buffer in bytes
276 * @param pw Pointer to buffer containing to which password
277 * copied
278 * @param pwlen The size of the password buffer in bytes
279 *
280 */
281 static void
auth_callback(SMBCCTX * context,const char * server_name,const char * share_name,char * domain_out,int domainmaxlen,char * username_out,int unmaxlen,char * password_out,int pwmaxlen)282 auth_callback (SMBCCTX *context,
283 const char *server_name, const char *share_name,
284 char *domain_out, int domainmaxlen,
285 char *username_out, int unmaxlen,
286 char *password_out, int pwmaxlen)
287 {
288 GVfsBackendSmbBrowse *backend;
289 char *ask_password, *ask_user, *ask_domain;
290 gboolean handled, abort;
291
292 backend = smbc_getOptionUserData (context);
293
294 strncpy (password_out, "", pwmaxlen);
295
296 if (backend->domain)
297 strncpy (domain_out, backend->domain, domainmaxlen);
298 if (backend->user)
299 strncpy (username_out, backend->user, unmaxlen);
300
301 if (backend->mount_cancelled)
302 {
303 /* Don't prompt for credentials, let smbclient finish the mount loop */
304 strncpy (username_out, "ABORT", unmaxlen);
305 strncpy (password_out, "", pwmaxlen);
306 g_debug ("auth_callback - mount_cancelled\n");
307 return;
308 }
309
310 if (backend->mount_source == NULL)
311 {
312 /* Not during mount, use last password */
313 if (backend->last_user)
314 strncpy (username_out, backend->last_user, unmaxlen);
315 if (backend->last_domain)
316 strncpy (domain_out, backend->last_domain, domainmaxlen);
317 if (backend->last_password)
318 strncpy (password_out, backend->last_password, pwmaxlen);
319 return;
320 }
321
322 if (backend->mount_try == 0 &&
323 backend->user == NULL &&
324 backend->domain == NULL)
325 {
326 /* Try again if kerberos login + anonymous fallback fails */
327 backend->mount_try_again = TRUE;
328 g_debug ("auth_callback - anonymous pass\n");
329 }
330 else
331 {
332 gboolean in_keyring = FALSE;
333
334 g_debug ("auth_callback - normal pass\n");
335
336 if (!backend->password_in_keyring)
337 {
338 in_keyring = g_vfs_keyring_lookup_password (backend->user,
339 backend->server,
340 backend->domain,
341 "smb",
342 NULL,
343 NULL,
344 backend->port != -1 ? backend->port : 0,
345 &ask_user,
346 &ask_domain,
347 &ask_password);
348 backend->password_in_keyring = in_keyring;
349
350 if (in_keyring)
351 g_debug ("auth_callback - reusing keyring credentials: user = '%s', domain = '%s'\n",
352 ask_user ? ask_user : "NULL",
353 ask_domain ? ask_domain : "NULL");
354 }
355
356 if (!in_keyring)
357 {
358 GAskPasswordFlags flags = G_ASK_PASSWORD_NEED_PASSWORD;
359 char *message;
360
361 if (g_vfs_keyring_is_available ())
362 flags |= G_ASK_PASSWORD_SAVING_SUPPORTED;
363 if (backend->domain == NULL)
364 flags |= G_ASK_PASSWORD_NEED_DOMAIN;
365 if (backend->user == NULL)
366 flags |= G_ASK_PASSWORD_NEED_USERNAME;
367
368 g_debug ("auth_callback - asking for password...\n");
369
370 /* translators: %s is a server name */
371 message = g_strdup_printf (_("Password required for %s"),
372 server_name);
373 handled = g_mount_source_ask_password (backend->mount_source,
374 message,
375 username_out,
376 domain_out,
377 flags,
378 &abort,
379 &ask_password,
380 &ask_user,
381 &ask_domain,
382 NULL,
383 &(backend->password_save));
384 g_free (message);
385 if (!handled)
386 goto out;
387
388 if (abort)
389 {
390 strncpy (username_out, "ABORT", unmaxlen);
391 strncpy (password_out, "", pwmaxlen);
392 backend->mount_cancelled = TRUE;
393 goto out;
394 }
395 }
396
397 /* Try again if this fails */
398 backend->mount_try_again = TRUE;
399
400 strncpy (password_out, ask_password, pwmaxlen);
401 if (ask_user && *ask_user)
402 strncpy (username_out, ask_user, unmaxlen);
403 if (ask_domain && *ask_domain)
404 strncpy (domain_out, ask_domain, domainmaxlen);
405
406 out:
407 g_free (ask_password);
408 g_free (ask_user);
409 g_free (ask_domain);
410 }
411
412 backend->last_user = g_strdup (username_out);
413 backend->last_domain = g_strdup (domain_out);
414 backend->last_password = g_strdup (password_out);
415 g_debug ("auth_callback - out: last_user = '%s', last_domain = '%s'\n",
416 backend->last_user, backend->last_domain);
417 }
418
419 /* Add a server to the cache system
420 *
421 * @param c pointer to smb context
422 * @param srv pointer to server to add
423 * @param server server name
424 * @param share share name
425 * @param workgroup workgroup used to connect
426 * @param username username used to connect
427 * @return 0 on success. 1 on failure.
428 *
429 */
430 static int
add_cached_server(SMBCCTX * context,SMBCSRV * new,const char * server_name,const char * share_name,const char * domain,const char * username)431 add_cached_server (SMBCCTX *context, SMBCSRV *new,
432 const char *server_name, const char *share_name,
433 const char *domain, const char *username)
434 {
435 CachedServer *cached_server;
436
437 cached_server = g_new (CachedServer, 1);
438 cached_server->server_name = g_strdup (server_name);
439 cached_server->share_name = g_strdup (share_name);
440 cached_server->domain = g_strdup (domain);
441 cached_server->username = g_strdup (username);
442
443 g_debug ("adding cached server '%s'\\'%s', user '%s';'%s' with data %p\n",
444 server_name ? server_name : "NULL",
445 share_name ? share_name : "(no share)",
446 domain ? domain : "(no domain)",
447 username ? username : "NULL",
448 new);
449
450 if (server_cache == NULL)
451 server_cache = g_hash_table_new_full (cached_server_hash, cached_server_equal,
452 (GDestroyNotify)cached_server_free, NULL);
453
454 g_hash_table_insert (server_cache, cached_server, new);
455
456 return 0;
457 }
458
459 static gboolean
remove_cb(gpointer key,gpointer value,gpointer user_data)460 remove_cb (gpointer key,
461 gpointer value,
462 gpointer user_data)
463 {
464 return value == user_data;
465 }
466
467 /* Remove cached server
468 *
469 * @param c pointer to smb context
470 * @param srv pointer to server to remove
471 * @return 0 when found and removed. 1 on failure.
472 *
473 */
474 static int
remove_cached_server(SMBCCTX * context,SMBCSRV * server)475 remove_cached_server (SMBCCTX * context, SMBCSRV * server)
476 {
477 guint num;
478
479 if (server_cache)
480 {
481 g_debug ("removing cached servers with data %p\n", server);
482 num = g_hash_table_foreach_remove (server_cache, remove_cb, server);
483 if (num != 0)
484 return 0;
485 }
486
487 return 1;
488 }
489
490 /* Look up a server in the cache system
491 *
492 * @param c pointer to smb context
493 * @param server server name to match
494 * @param share share name to match
495 * @param workgroup workgroup to match
496 * @param username username to match
497 * @return pointer to SMBCSRV on success. NULL on failure.
498 *
499 */
500 static SMBCSRV *
get_cached_server(SMBCCTX * context,const char * server_name,const char * share_name,const char * domain,const char * username)501 get_cached_server (SMBCCTX * context,
502 const char *server_name, const char *share_name,
503 const char *domain, const char *username)
504 {
505 const CachedServer key = {
506 (char *)server_name,
507 (char *)share_name,
508 (char *)domain,
509 (char *)username
510 };
511 SMBCSRV *ret = NULL;
512
513 g_debug ("looking up cached server '%s'\\'%s', user '%s';'%s'\n",
514 server_name ? server_name : "NULL",
515 share_name ? share_name : "(no share)",
516 domain ? domain : "(no domain)",
517 username ? username : "NULL");
518
519 if (server_cache)
520 ret = g_hash_table_lookup (server_cache, &key);
521
522 g_debug (" returning %p\n", ret);
523 return ret;
524 }
525
526 /* Try to remove all servers from the cache system and disconnect
527 *
528 * @param c pointer to smb context
529 *
530 * @return 0 when found and removed. 1 on failure.
531 *
532 */
533 static int
purge_cached(SMBCCTX * context)534 purge_cached (SMBCCTX * context)
535 {
536 g_debug ("purging server cache\n");
537
538 if (server_cache)
539 g_hash_table_remove_all (server_cache);
540
541 return 0;
542 }
543
544 static gboolean
update_cache(GVfsBackendSmbBrowse * backend,SMBCFILE * supplied_dir)545 update_cache (GVfsBackendSmbBrowse *backend, SMBCFILE *supplied_dir)
546 {
547 char *uri;
548 char dirents[1024*4];
549 struct smbc_dirent *dirp;
550 GList *entries;
551 SMBCFILE *dir;
552 int res;
553 smbc_opendir_fn smbc_opendir;
554 smbc_getdents_fn smbc_getdents;
555 smbc_closedir_fn smbc_closedir;
556
557
558 entries = NULL;
559 res = -1;
560
561 g_mutex_lock (&backend->update_cache_lock);
562
563 g_debug ("update_cache - updating...\n");
564
565 /* Update Cache */
566
567 smbc_opendir = smbc_getFunctionOpendir (backend->smb_context);
568 smbc_getdents = smbc_getFunctionGetdents (backend->smb_context);
569 smbc_closedir = smbc_getFunctionClosedir (backend->smb_context);
570
571 uri = create_smb_uri (backend->server, backend->port, NULL, NULL);
572 dir = supplied_dir ? supplied_dir : smbc_opendir (backend->smb_context, uri);
573 g_free (uri);
574 if (dir == NULL)
575 {
576 goto out;
577 }
578
579 while (TRUE)
580 {
581 res = smbc_getdents (backend->smb_context, dir, (struct smbc_dirent *)dirents, sizeof (dirents));
582 if (res <= 0)
583 {
584 if (res < 0)
585 g_debug ("update_cache - smbc_getdents returned %d, errno = [%d] %s\n",
586 res, errno, g_strerror (errno));
587 break;
588 }
589
590 dirp = (struct smbc_dirent *)dirents;
591 while (res > 0)
592 {
593 unsigned int dirlen;
594
595 if (dirp->smbc_type != SMBC_IPC_SHARE &&
596 dirp->smbc_type != SMBC_COMMS_SHARE &&
597 dirp->smbc_type != SMBC_PRINTER_SHARE &&
598 strcmp (dirp->name, ".") != 0 &&
599 strcmp (dirp->name, "..") != 0)
600 {
601 BrowseEntry *entry = g_new (BrowseEntry, 1);
602 gboolean valid_utf8;
603
604 entry->smbc_type = dirp->smbc_type;
605 entry->name = g_strdup (dirp->name);
606 entry->name_utf8 = smb_name_to_utf8 (dirp->name, &valid_utf8);
607 entry->name_normalized = normalize_smb_name_helper (dirp->name, -1, valid_utf8);
608 entry->comment = smb_name_to_utf8 (dirp->comment, NULL);
609
610 entries = g_list_prepend (entries, entry);
611 }
612
613 dirlen = dirp->dirlen;
614 dirp = (struct smbc_dirent *) (((char *)dirp) + dirlen);
615 res -= dirlen;
616 }
617
618 entries = g_list_reverse (entries);
619 }
620
621 if (! supplied_dir)
622 smbc_closedir (backend->smb_context, dir);
623
624
625 out:
626
627 g_mutex_lock (&backend->entries_lock);
628
629 /* Clear old cache */
630 g_list_free_full (backend->entries, (GDestroyNotify)browse_entry_free);
631 backend->entries = entries;
632 backend->last_entry_update = time (NULL);
633
634 g_debug ("update_cache - done.\n");
635
636 g_mutex_unlock (&backend->entries_lock);
637 g_mutex_unlock (&backend->update_cache_lock);
638
639 return (res >= 0);
640 }
641
642 static BrowseEntry *
find_entry_unlocked(GVfsBackendSmbBrowse * backend,const char * filename)643 find_entry_unlocked (GVfsBackendSmbBrowse *backend,
644 const char *filename)
645 {
646 BrowseEntry *entry, *found;
647 GList *l;
648 char *end;
649 int len;
650 char *normalized;
651
652 while (*filename == '/')
653 filename++;
654
655 end = strchr (filename, '/');
656 if (end)
657 {
658 len = end - filename;
659
660 while (*end == '/')
661 end++;
662
663 if (*end != 0)
664 return NULL;
665 }
666 else
667 len = strlen (filename);
668
669 /* First look for an exact filename match */
670 found = NULL;
671 for (l = backend->entries; l != NULL; l = l->next)
672 {
673 entry = l->data;
674
675 if (strncmp (filename, entry->name, len) == 0 &&
676 strlen (entry->name) == len)
677 {
678 found = entry;
679 break;
680 }
681 }
682
683 if (found == NULL)
684 {
685 /* That failed, try normalizing the filename */
686 normalized = normalize_smb_name (filename, len);
687
688 for (l = backend->entries; l != NULL; l = l->next)
689 {
690 entry = l->data;
691
692 if (strcmp (normalized, entry->name_normalized) == 0)
693 {
694 found = entry;
695 break;
696 }
697 }
698 g_free (normalized);
699 }
700
701 return found;
702 }
703
704 static GMountSpec *
get_mount_spec_for_share(const char * server,int port,const char * share)705 get_mount_spec_for_share (const char *server,
706 int port,
707 const char *share)
708 {
709 GMountSpec *mount_spec;
710 char *normalized;
711 char *port_str;
712
713 mount_spec = g_mount_spec_new ("smb-share");
714 normalized = normalize_smb_name (server, -1);
715 g_mount_spec_set (mount_spec, "server", normalized);
716 g_free (normalized);
717 normalized = normalize_smb_name (share, -1);
718 g_mount_spec_set (mount_spec, "share", normalized);
719 g_free (normalized);
720 if (port != -1)
721 {
722 port_str = g_strdup_printf ("%d", port);
723 g_mount_spec_set (mount_spec, "port", port_str);
724 g_free (port_str);
725 }
726
727 return mount_spec;
728 }
729
730 static gboolean
is_root(const char * filename)731 is_root (const char *filename)
732 {
733 const char *p;
734
735 p = filename;
736 while (*p == '/')
737 p++;
738
739 return *p == 0;
740 }
741
742 static gboolean
has_name(GVfsBackendSmbBrowse * backend,const char * filename)743 has_name (GVfsBackendSmbBrowse *backend,
744 const char *filename)
745 {
746 gboolean res;
747
748 g_mutex_lock (&backend->entries_lock);
749 res = (find_entry_unlocked (backend, filename) != NULL);
750 g_mutex_unlock (&backend->entries_lock);
751 return res;
752 }
753
754 static gboolean
cache_needs_updating(GVfsBackendSmbBrowse * backend)755 cache_needs_updating (GVfsBackendSmbBrowse *backend)
756 {
757 time_t now;
758 gboolean res;
759
760 /* If there's already cache update in progress, lock and wait until update is finished, then recheck */
761 g_mutex_lock (&backend->update_cache_lock);
762 now = time (NULL);
763 res = now < backend->last_entry_update ||
764 (now - backend->last_entry_update) > DEFAULT_CACHE_EXPIRATION_TIME;
765 g_mutex_unlock (&backend->update_cache_lock);
766
767 return res;
768 }
769
770 static void
do_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)771 do_mount (GVfsBackend *backend,
772 GVfsJobMount *job,
773 GMountSpec *mount_spec,
774 GMountSource *mount_source,
775 gboolean is_automount)
776 {
777 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
778 SMBCCTX *smb_context;
779 SMBCFILE *dir;
780 char *display_name;
781 const char *debug;
782 int debug_val;
783 char *icon;
784 char *symbolic_icon;
785 gchar *port_str;
786 char *uri;
787 gboolean res;
788 GMountSpec *browse_mount_spec;
789 smbc_opendir_fn smbc_opendir;
790 smbc_closedir_fn smbc_closedir;
791 int errsv;
792
793 smb_context = smbc_new_context ();
794 if (smb_context == NULL)
795 {
796 g_vfs_job_failed (G_VFS_JOB (job),
797 G_IO_ERROR, G_IO_ERROR_FAILED,
798 _("Internal Error (%s)"), "Failed to allocate smb context");
799 return;
800 }
801
802 smbc_setOptionUserData (smb_context, backend);
803
804 debug = g_getenv ("GVFS_SMB_DEBUG");
805 if (debug)
806 debug_val = atoi (debug);
807 else
808 debug_val = 0;
809
810 smbc_setDebug (smb_context, debug_val);
811 smbc_setFunctionAuthDataWithContext (smb_context, auth_callback);
812
813 smbc_setFunctionAddCachedServer (smb_context, add_cached_server);
814 smbc_setFunctionGetCachedServer (smb_context, get_cached_server);
815 smbc_setFunctionRemoveCachedServer (smb_context, remove_cached_server);
816 smbc_setFunctionPurgeCachedServers (smb_context, purge_cached);
817
818 if (op_backend->default_workgroup != NULL)
819 smbc_setWorkgroup (smb_context, op_backend->default_workgroup);
820
821 /* Initial settings:
822 * - use Kerberos (always)
823 * - in case of no username specified, try anonymous login
824 */
825 smbc_setOptionUseKerberos (smb_context, 1);
826 smbc_setOptionFallbackAfterKerberos (smb_context, op_backend->user != NULL);
827 smbc_setOptionNoAutoAnonymousLogin (smb_context, op_backend->user != NULL);
828
829 #if 0
830 smbc_setOptionDebugToStderr (smb_context, 1);
831 #endif
832
833 if (!smbc_init_context (smb_context))
834 {
835 g_vfs_job_failed (G_VFS_JOB (job),
836 G_IO_ERROR, G_IO_ERROR_FAILED,
837 _("Internal Error (%s)"), "Failed to initialize smb context");
838 smbc_free_context (smb_context, FALSE);
839 return;
840 }
841
842 op_backend->smb_context = smb_context;
843
844 /* Convert DEFAULT_WORKGROUP_NAME to real domain */
845 if (op_backend->mounted_server != NULL &&
846 g_ascii_strcasecmp (op_backend->mounted_server, DEFAULT_WORKGROUP_NAME) == 0)
847 op_backend->server = g_strdup (smbc_getWorkgroup (smb_context));
848 else
849 op_backend->server = g_strdup (op_backend->mounted_server);
850
851 #ifdef HAVE_SMBC_SETOPTIONPROTOCOLS
852 /* Force NT1 protocol version if server can't be resolved (i.e. is not
853 * hostname, nor IP address). This is needed for workgroup support, because
854 * "client max protocol" has been changed from NT1 to SMB3 in recent samba
855 * versions.
856 */
857
858 if (op_backend->server != NULL)
859 {
860 GResolver *resolver;
861 GList *addresses;
862 GError *error = NULL;
863 gchar *server;
864
865 resolver = g_resolver_get_default ();
866
867 /* IPv6 server includes brackets in GMountSpec, GResolver doesn't */
868 if (gvfs_is_ipv6 (op_backend->server))
869 server = g_strndup (op_backend->server + 1, strlen (op_backend->server) - 2);
870 else
871 server = g_strdup (op_backend->server);
872
873 addresses = g_resolver_lookup_by_name (resolver, server, NULL, &error);
874 if (addresses == NULL)
875 {
876 if (error != NULL)
877 {
878 g_debug ("%s\n", error->message);
879 g_error_free (error);
880 }
881
882 g_debug ("Forcing NT1 protocol version\n");
883 smbc_setOptionProtocols (smb_context, "NT1", "NT1");
884 }
885
886 g_resolver_free_addresses (addresses);
887 g_object_unref (resolver);
888 g_free (server);
889 }
890 #endif
891
892 icon = NULL;
893 symbolic_icon = NULL;
894 if (op_backend->server == NULL)
895 {
896 display_name = g_strdup (_("Windows Network"));
897 browse_mount_spec = g_mount_spec_new ("smb-network");
898 icon = "network-workgroup";
899 symbolic_icon = "network-workgroup-symbolic";
900 }
901 else
902 {
903 /* translators: Name for the location that lists the smb shares
904 availible on a server (%s is the name of the server) */
905 display_name = g_strdup_printf (_("Windows shares on %s"), op_backend->server);
906 browse_mount_spec = g_mount_spec_new ("smb-server");
907 g_mount_spec_set (browse_mount_spec, "server", op_backend->mounted_server);
908 if (op_backend->port != -1)
909 {
910 port_str = g_strdup_printf ("%d", op_backend->port);
911 g_mount_spec_set (browse_mount_spec, "port", port_str);
912 g_free (port_str);
913 }
914 icon = "network-server";
915 symbolic_icon = "network-server-symbolic";
916 }
917
918 if (op_backend->user)
919 g_mount_spec_set (browse_mount_spec, "user", op_backend->user);
920 if (op_backend->domain)
921 g_mount_spec_set (browse_mount_spec, "domain", op_backend->domain);
922
923 g_vfs_backend_set_display_name (backend, display_name);
924 g_free (display_name);
925 if (icon)
926 g_vfs_backend_set_icon_name (backend, icon);
927 if (symbolic_icon)
928 g_vfs_backend_set_symbolic_icon_name (backend, symbolic_icon);
929 g_vfs_backend_set_user_visible (backend, FALSE);
930 g_vfs_backend_set_mount_spec (backend, browse_mount_spec);
931 g_mount_spec_unref (browse_mount_spec);
932
933 op_backend->mount_source = mount_source;
934 op_backend->mount_try = 0;
935 op_backend->password_save = G_PASSWORD_SAVE_NEVER;
936
937 smbc_opendir = smbc_getFunctionOpendir (smb_context);
938 smbc_closedir = smbc_getFunctionClosedir (smb_context);
939
940 uri = create_smb_uri (op_backend->server, op_backend->port, NULL, NULL);
941
942 g_debug ("do_mount - URI = %s\n", uri);
943
944 errsv = 0;
945
946 do
947 {
948 op_backend->mount_try_again = FALSE;
949 op_backend->mount_cancelled = FALSE;
950
951 g_debug ("do_mount - try #%d \n", op_backend->mount_try);
952
953 dir = smbc_opendir (smb_context, uri);
954
955 errsv = errno;
956 g_debug ("do_mount - [%s; %d] dir = %p, cancelled = %d, errno = [%d] '%s' \n",
957 uri, op_backend->mount_try, dir, op_backend->mount_cancelled,
958 errsv, g_strerror (errsv));
959
960 if (dir == NULL &&
961 (op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
962 {
963 g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
964 break;
965 }
966
967 if (dir != NULL)
968 {
969 /* Let update_cache() do enumeration, check for the smbc_getdents() result */
970 res = update_cache (op_backend, dir);
971 smbc_closedir (smb_context, dir);
972 g_debug ("do_mount - login successful, res = %d\n", res);
973 if (res)
974 break;
975 }
976 else {
977 /* Purge the cache, we need to have clean playground for next auth try */
978 purge_cached (smb_context);
979 }
980
981 /* The first round is Kerberos-only. Only if this fails do we enable
982 * NTLMSSP fallback (turning off anonymous fallback, which we've
983 * already tried and failed with).
984 */
985 if (op_backend->mount_try == 0)
986 {
987 g_debug ("do_mount - after anon, enabling NTLMSSP fallback\n");
988 smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
989 smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context, 1);
990 }
991 op_backend->mount_try++;
992 }
993 while (op_backend->mount_try_again);
994
995 g_free (uri);
996
997 op_backend->mount_source = NULL;
998
999 if (dir == NULL)
1000 {
1001 if (op_backend->mount_cancelled)
1002 g_vfs_job_failed (G_VFS_JOB (job),
1003 G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED,
1004 _("Password dialog cancelled"));
1005 else
1006 g_vfs_job_failed (G_VFS_JOB (job),
1007 G_IO_ERROR, G_IO_ERROR_FAILED,
1008 /* translators: We tried to mount a windows (samba) share, but failed */
1009 _("Failed to retrieve share list from server: %s"), g_strerror (errsv));
1010
1011 return;
1012 }
1013
1014 g_vfs_keyring_save_password (op_backend->last_user,
1015 op_backend->server,
1016 op_backend->last_domain,
1017 "smb",
1018 NULL,
1019 NULL,
1020 0,
1021 op_backend->last_password,
1022 op_backend->password_save);
1023
1024 g_vfs_job_succeeded (G_VFS_JOB (job));
1025 }
1026
1027 static gboolean
try_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)1028 try_mount (GVfsBackend *backend,
1029 GVfsJobMount *job,
1030 GMountSpec *mount_spec,
1031 GMountSource *mount_source,
1032 gboolean is_automount)
1033 {
1034 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1035 const char *server;
1036 const char *user, *domain;
1037 const char *port;
1038 int port_num;
1039
1040 if (strcmp (g_mount_spec_get_type (mount_spec), "smb-network") == 0)
1041 server = NULL;
1042 else
1043 {
1044 server = g_mount_spec_get (mount_spec, "server");
1045 if (server == NULL)
1046 {
1047 g_vfs_job_failed (G_VFS_JOB (job),
1048 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1049 "No server specified for smb-server share");
1050 return TRUE;
1051 }
1052 }
1053
1054 user = g_mount_spec_get (mount_spec, "user");
1055 domain = g_mount_spec_get (mount_spec, "domain");
1056 port = g_mount_spec_get (mount_spec, "port");
1057
1058 if (is_automount &&
1059 ((user != NULL) || (domain != NULL)))
1060 {
1061 g_vfs_job_failed (G_VFS_JOB (job),
1062 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1063 "Can't automount smb browsing with specified user or domain");
1064 return TRUE;
1065 }
1066
1067 op_backend->user = g_strdup (user);
1068 op_backend->domain = g_strdup (domain);
1069 op_backend->mounted_server = g_strdup (server);
1070 if (port && (port_num = atoi (port)))
1071 op_backend->port = port_num;
1072 else
1073 op_backend->port = -1;
1074
1075 return FALSE;
1076 }
1077
1078 static void
run_mount_mountable(GVfsBackendSmbBrowse * backend,GVfsJobMountMountable * job,const char * filename,GMountSource * mount_source)1079 run_mount_mountable (GVfsBackendSmbBrowse *backend,
1080 GVfsJobMountMountable *job,
1081 const char *filename,
1082 GMountSource *mount_source)
1083 {
1084 BrowseEntry *entry;
1085 GError *error = NULL;
1086 GMountSpec *mount_spec;
1087
1088 g_mutex_lock (&backend->entries_lock);
1089
1090 entry = find_entry_unlocked (backend, filename);
1091
1092 if (entry)
1093 {
1094 if (backend->server != NULL &&
1095 entry->smbc_type == SMBC_FILE_SHARE)
1096 {
1097 mount_spec = get_mount_spec_for_share (backend->server, backend->port, entry->name);
1098 g_vfs_job_mount_mountable_set_target (job, mount_spec, "/", TRUE);
1099 g_mount_spec_unref (mount_spec);
1100 }
1101 else
1102 g_set_error_literal (&error,
1103 G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
1104 _("Not a mountable file"));
1105 }
1106 else
1107 g_set_error_literal (&error,
1108 G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1109 _("File doesn’t exist"));
1110
1111 g_mutex_unlock (&backend->entries_lock);
1112
1113 if (error)
1114 {
1115 g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
1116 g_error_free (error);
1117 }
1118 else
1119 g_vfs_job_succeeded (G_VFS_JOB (job));
1120 }
1121
1122 static void
do_mount_mountable(GVfsBackend * backend,GVfsJobMountMountable * job,const char * filename,GMountSource * mount_source)1123 do_mount_mountable (GVfsBackend *backend,
1124 GVfsJobMountMountable *job,
1125 const char *filename,
1126 GMountSource *mount_source)
1127 {
1128 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1129
1130 update_cache (op_backend, NULL);
1131
1132 run_mount_mountable (op_backend,
1133 job,
1134 filename,
1135 mount_source);
1136 }
1137
1138 static gboolean
try_mount_mountable(GVfsBackend * backend,GVfsJobMountMountable * job,const char * filename,GMountSource * mount_source)1139 try_mount_mountable (GVfsBackend *backend,
1140 GVfsJobMountMountable *job,
1141 const char *filename,
1142 GMountSource *mount_source)
1143 {
1144 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1145
1146 if (is_root (filename))
1147 {
1148 g_vfs_job_failed (G_VFS_JOB (job),
1149 G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
1150 _("Not a mountable file"));
1151 return TRUE;
1152 }
1153
1154 if (cache_needs_updating (op_backend))
1155 return FALSE;
1156
1157 run_mount_mountable (op_backend,
1158 job,
1159 filename,
1160 mount_source);
1161 return TRUE;
1162 }
1163
1164 static void
run_open_for_read(GVfsBackendSmbBrowse * backend,GVfsJobOpenForRead * job,const char * filename)1165 run_open_for_read (GVfsBackendSmbBrowse *backend,
1166 GVfsJobOpenForRead *job,
1167 const char *filename)
1168 {
1169 if (has_name (backend, filename))
1170 g_vfs_job_failed (G_VFS_JOB (job),
1171 G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1172 _("Not a regular file"));
1173 else
1174 g_vfs_job_failed (G_VFS_JOB (job),
1175 G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1176 _("File doesn’t exist"));
1177 }
1178
1179 static void
do_open_for_read(GVfsBackend * backend,GVfsJobOpenForRead * job,const char * filename)1180 do_open_for_read (GVfsBackend *backend,
1181 GVfsJobOpenForRead *job,
1182 const char *filename)
1183 {
1184 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1185
1186 update_cache (op_backend, NULL);
1187
1188 run_open_for_read (op_backend, job, filename);
1189 }
1190
1191 static gboolean
try_open_for_read(GVfsBackend * backend,GVfsJobOpenForRead * job,const char * filename)1192 try_open_for_read (GVfsBackend *backend,
1193 GVfsJobOpenForRead *job,
1194 const char *filename)
1195 {
1196 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1197
1198 if (cache_needs_updating (op_backend))
1199 return FALSE;
1200
1201 run_open_for_read (op_backend, job, filename);
1202
1203 return TRUE;
1204 }
1205
1206 static gboolean
try_read(GVfsBackend * backend,GVfsJobRead * job,GVfsBackendHandle handle,char * buffer,gsize bytes_requested)1207 try_read (GVfsBackend *backend,
1208 GVfsJobRead *job,
1209 GVfsBackendHandle handle,
1210 char *buffer,
1211 gsize bytes_requested)
1212 {
1213 g_vfs_job_failed (G_VFS_JOB (job),
1214 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1215 "Invalid argument");
1216
1217 return TRUE;
1218 }
1219
1220 static gboolean
try_seek_on_read(GVfsBackend * backend,GVfsJobSeekRead * job,GVfsBackendHandle handle,goffset offset,GSeekType type)1221 try_seek_on_read (GVfsBackend *backend,
1222 GVfsJobSeekRead *job,
1223 GVfsBackendHandle handle,
1224 goffset offset,
1225 GSeekType type)
1226 {
1227 g_vfs_job_failed (G_VFS_JOB (job),
1228 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1229 "Invalid argument");
1230 return TRUE;
1231 }
1232
1233 static gboolean
try_close_read(GVfsBackend * backend,GVfsJobCloseRead * job,GVfsBackendHandle handle)1234 try_close_read (GVfsBackend *backend,
1235 GVfsJobCloseRead *job,
1236 GVfsBackendHandle handle)
1237 {
1238 g_vfs_job_failed (G_VFS_JOB (job),
1239 G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1240 "Invalid argument");
1241 return TRUE;
1242 }
1243
1244 static void
get_file_info_from_entry(GVfsBackendSmbBrowse * backend,BrowseEntry * entry,GFileInfo * info)1245 get_file_info_from_entry (GVfsBackendSmbBrowse *backend, BrowseEntry *entry, GFileInfo *info)
1246 {
1247 GMountSpec *mount_spec;
1248 GString *uri;
1249 GIcon *icon;
1250 GIcon *symbolic_icon;
1251
1252 g_file_info_set_name (info, entry->name);
1253 g_file_info_set_display_name (info, entry->name_utf8);
1254 g_file_info_set_edit_name (info, entry->name_utf8);
1255 g_file_info_set_attribute_string (info, "smb::comment", entry->comment);
1256 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
1257 g_file_info_set_content_type (info, "inode/directory");
1258
1259 icon = NULL;
1260 if (entry->smbc_type == SMBC_WORKGROUP)
1261 {
1262 icon = g_themed_icon_new ("network-workgroup");
1263 symbolic_icon = g_themed_icon_new ("network-workgroup-symbolic");
1264 }
1265 else if (entry->smbc_type == SMBC_SERVER)
1266 {
1267 icon = g_themed_icon_new ("network-server");
1268 symbolic_icon = g_themed_icon_new ("network-server-symbolic");
1269 }
1270 else
1271 {
1272 icon = g_themed_icon_new ("folder-remote");
1273 symbolic_icon = g_themed_icon_new ("folder-remote-symbolic");
1274 }
1275
1276 if (icon)
1277 {
1278 g_file_info_set_icon (info, icon);
1279 g_object_unref (icon);
1280 }
1281 if (symbolic_icon)
1282 {
1283 g_file_info_set_symbolic_icon (info, symbolic_icon);
1284 g_object_unref (symbolic_icon);
1285 }
1286
1287 mount_spec = NULL;
1288 if (backend->server)
1289 {
1290 /* browsing server/workgroup */
1291 if (entry->smbc_type == SMBC_WORKGROUP ||
1292 entry->smbc_type == SMBC_SERVER)
1293 {
1294 uri = g_string_new ("smb://");
1295 g_string_append_uri_escaped (uri, entry->name, NULL, FALSE);
1296 g_string_append_c (uri, '/');
1297 }
1298 else
1299 {
1300 mount_spec = get_mount_spec_for_share (backend->server, backend->port, entry->name);
1301
1302 uri = g_string_new ("smb://");
1303 g_string_append_uri_escaped (uri, backend->server, NULL, FALSE);
1304 g_string_append_c (uri, '/');
1305 g_string_append_uri_escaped (uri, entry->name, NULL, FALSE);
1306 }
1307 }
1308 else
1309 {
1310 /* browsing network */
1311 uri = g_string_new ("smb://");
1312 g_string_append_uri_escaped (uri, entry->name, NULL, FALSE);
1313 g_string_append_c (uri, '/');
1314
1315 /* these are auto-mounted, so no CAN_MOUNT/UNMOUNT */
1316 }
1317
1318 if (mount_spec)
1319 {
1320 g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE);
1321 if (g_mount_tracker_has_mount_spec (mount_tracker, mount_spec))
1322 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, FALSE);
1323 else
1324 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, TRUE);
1325 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, FALSE);
1326 g_mount_spec_unref (mount_spec);
1327 }
1328 else
1329 g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT);
1330
1331 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, uri->str);
1332 g_string_free (uri, TRUE);
1333 }
1334
1335 static void
run_query_info(GVfsBackendSmbBrowse * backend,GVfsJobQueryInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * matcher)1336 run_query_info (GVfsBackendSmbBrowse *backend,
1337 GVfsJobQueryInfo *job,
1338 const char *filename,
1339 GFileInfo *info,
1340 GFileAttributeMatcher *matcher)
1341 {
1342 BrowseEntry *entry;
1343
1344 g_mutex_lock (&backend->entries_lock);
1345
1346 entry = find_entry_unlocked (backend, filename);
1347
1348 if (entry)
1349 get_file_info_from_entry (backend, entry, info);
1350
1351 g_mutex_unlock (&backend->entries_lock);
1352
1353 if (entry)
1354 g_vfs_job_succeeded (G_VFS_JOB (job));
1355 else
1356 g_vfs_job_failed (G_VFS_JOB (job),
1357 G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1358 _("File doesn’t exist"));
1359 }
1360
1361 static void
do_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * matcher)1362 do_query_info (GVfsBackend *backend,
1363 GVfsJobQueryInfo *job,
1364 const char *filename,
1365 GFileQueryInfoFlags flags,
1366 GFileInfo *info,
1367 GFileAttributeMatcher *matcher)
1368 {
1369 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1370
1371 update_cache (op_backend, NULL);
1372
1373 run_query_info (op_backend, job, filename, info, matcher);
1374 }
1375
1376
1377 static gboolean
try_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * matcher)1378 try_query_info (GVfsBackend *backend,
1379 GVfsJobQueryInfo *job,
1380 const char *filename,
1381 GFileQueryInfoFlags flags,
1382 GFileInfo *info,
1383 GFileAttributeMatcher *matcher)
1384 {
1385 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1386 GIcon *icon;
1387
1388 if (is_root (filename))
1389 {
1390 g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
1391 g_file_info_set_name (info, "/");
1392 g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend));
1393 g_file_info_set_content_type (info, "inode/directory");
1394 icon = g_vfs_backend_get_icon (backend);
1395 if (icon != NULL)
1396 g_file_info_set_icon (info, icon);
1397 icon = g_vfs_backend_get_symbolic_icon (backend);
1398 if (icon != NULL)
1399 g_file_info_set_symbolic_icon (info, icon);
1400 g_vfs_job_succeeded (G_VFS_JOB (job));
1401
1402 return TRUE;
1403 }
1404
1405 if (cache_needs_updating (op_backend))
1406 return FALSE;
1407
1408 run_query_info (op_backend, job, filename, info, matcher);
1409
1410 return TRUE;
1411 }
1412
1413 static void
run_enumerate(GVfsBackendSmbBrowse * backend,GVfsJobEnumerate * job,const char * filename,GFileAttributeMatcher * matcher)1414 run_enumerate (GVfsBackendSmbBrowse *backend,
1415 GVfsJobEnumerate *job,
1416 const char *filename,
1417 GFileAttributeMatcher *matcher)
1418 {
1419 GList *files, *l;
1420 GFileInfo *info;
1421
1422 if (!is_root (filename))
1423 {
1424 if (has_name (backend, filename))
1425 g_vfs_job_failed (G_VFS_JOB (job),
1426 G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
1427 _("Not a directory"));
1428 else
1429 g_vfs_job_failed (G_VFS_JOB (job),
1430 G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1431 _("File doesn’t exist"));
1432 return;
1433 }
1434
1435 /* TODO: limit requested to what we support */
1436 g_vfs_job_succeeded (G_VFS_JOB (job));
1437
1438 files = NULL;
1439 g_mutex_lock (&backend->entries_lock);
1440 for (l = backend->entries; l != NULL; l = l->next)
1441 {
1442 BrowseEntry *entry = l->data;
1443
1444 info = g_file_info_new ();
1445 get_file_info_from_entry (backend, entry, info);
1446
1447 files = g_list_prepend (files, info);
1448 }
1449 g_mutex_unlock (&backend->entries_lock);
1450
1451 files = g_list_reverse (files);
1452
1453 g_vfs_job_enumerate_add_infos (job, files);
1454 g_list_free_full (files, g_object_unref);
1455
1456 g_vfs_job_enumerate_done (job);
1457 }
1458
1459 static void
do_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * filename,GFileAttributeMatcher * matcher,GFileQueryInfoFlags flags)1460 do_enumerate (GVfsBackend *backend,
1461 GVfsJobEnumerate *job,
1462 const char *filename,
1463 GFileAttributeMatcher *matcher,
1464 GFileQueryInfoFlags flags)
1465 {
1466 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1467
1468 update_cache (op_backend, NULL);
1469
1470 run_enumerate (op_backend, job, filename, matcher);
1471 }
1472
1473 static gboolean
try_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * filename,GFileAttributeMatcher * matcher,GFileQueryInfoFlags flags)1474 try_enumerate (GVfsBackend *backend,
1475 GVfsJobEnumerate *job,
1476 const char *filename,
1477 GFileAttributeMatcher *matcher,
1478 GFileQueryInfoFlags flags)
1479 {
1480 GVfsBackendSmbBrowse *op_backend = G_VFS_BACKEND_SMB_BROWSE (backend);
1481
1482 if (cache_needs_updating (op_backend))
1483 return FALSE;
1484
1485 run_enumerate (op_backend, job, filename, matcher);
1486
1487 return TRUE;
1488 }
1489
1490 static gboolean
try_query_fs_info(GVfsBackend * backend,GVfsJobQueryFsInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * matcher)1491 try_query_fs_info (GVfsBackend *backend,
1492 GVfsJobQueryFsInfo *job,
1493 const char *filename,
1494 GFileInfo *info,
1495 GFileAttributeMatcher *matcher)
1496 {
1497 g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
1498 g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
1499 g_vfs_job_succeeded (G_VFS_JOB (job));
1500 return TRUE;
1501 }
1502
1503 static void
g_vfs_backend_smb_browse_class_init(GVfsBackendSmbBrowseClass * klass)1504 g_vfs_backend_smb_browse_class_init (GVfsBackendSmbBrowseClass *klass)
1505 {
1506 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1507 GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
1508
1509 gobject_class->finalize = g_vfs_backend_smb_browse_finalize;
1510
1511 backend_class->mount = do_mount;
1512 backend_class->try_mount = try_mount;
1513 backend_class->mount_mountable = do_mount_mountable;
1514 backend_class->try_mount_mountable = try_mount_mountable;
1515 backend_class->open_for_read = do_open_for_read;
1516 backend_class->try_open_for_read = try_open_for_read;
1517 backend_class->try_read = try_read;
1518 backend_class->try_seek_on_read = try_seek_on_read;
1519 backend_class->try_close_read = try_close_read;
1520 backend_class->query_info = do_query_info;
1521 backend_class->try_query_info = try_query_info;
1522 backend_class->try_query_fs_info = try_query_fs_info;
1523 backend_class->enumerate = do_enumerate;
1524 backend_class->try_enumerate = try_enumerate;
1525 }
1526
1527 void
g_vfs_smb_browse_daemon_init(void)1528 g_vfs_smb_browse_daemon_init (void)
1529 {
1530 g_set_application_name (_("Windows Network File System Service"));
1531 }
1532