1  /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) Carl-Anton Ingmarsson 2011 <ca.ingmarsson@gmail.com>
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: Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
21  */
22 
23 #include <config.h>
24 
25 #include <stdlib.h>
26 #include <glib/gstdio.h>
27 #include <glib/gi18n.h>
28 #include <gio/gio.h>
29 
30 #ifdef HAVE_GCRYPT
31 #include <gcrypt.h>
32 #endif
33 
34 #include "gvfsjobmount.h"
35 #include "gvfsjobunmount.h"
36 #include "gvfsjobqueryinfo.h"
37 #include "gvfsjobenumerate.h"
38 #include "gvfsjobmountmountable.h"
39 #include "gmounttracker.h"
40 
41 #include "gvfsafpserver.h"
42 #include "gvfsafpconnection.h"
43 
44 #include "gvfsbackendafpbrowse.h"
45 
46 struct _GVfsBackendAfpBrowseClass
47 {
48   GVfsBackendClass parent_class;
49 };
50 
51 struct _GVfsBackendAfpBrowse
52 {
53   GVfsBackend parent_instance;
54 
55   GNetworkAddress    *addr;
56   char               *user;
57 
58   GMountTracker      *mount_tracker;
59   GVfsAfpServer      *server;
60 
61   char               *logged_in_user;
62   GPtrArray          *volumes;
63 };
64 
65 
66 G_DEFINE_TYPE (GVfsBackendAfpBrowse, g_vfs_backend_afp_browse, G_VFS_TYPE_BACKEND);
67 
68 
69 static void
get_volumes_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)70 get_volumes_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
71 {
72   GVfsAfpServer *server = G_VFS_AFP_SERVER (source_object);
73   GTask *task = G_TASK (user_data);
74   GVfsBackendAfpBrowse *afp_backend;
75   GPtrArray *volumes;
76   GError *err = NULL;
77 
78   afp_backend = G_VFS_BACKEND_AFP_BROWSE (g_task_get_source_object (task));
79 
80   volumes = g_vfs_afp_server_get_volumes_finish (server, res, &err);
81   if (!volumes)
82   {
83     g_task_return_error (task, err);
84     g_object_unref (task);
85     return;
86   }
87 
88   if (afp_backend->volumes)
89     g_ptr_array_unref (afp_backend->volumes);
90   afp_backend->volumes = volumes;
91 
92   g_task_return_boolean (task, TRUE);
93   g_object_unref (task);
94 }
95 
96 static void
update_cache(GVfsBackendAfpBrowse * afp_backend,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)97 update_cache (GVfsBackendAfpBrowse *afp_backend,
98               GCancellable *cancellable,
99               GAsyncReadyCallback callback,
100               gpointer user_data)
101 {
102   GTask *task;
103 
104   task = g_task_new (afp_backend, cancellable, callback, user_data);
105   g_task_set_source_tag (task, update_cache);
106 
107   g_vfs_afp_server_get_volumes (afp_backend->server, cancellable, get_volumes_cb, task);
108 }
109 
110 static gboolean
update_cache_finish(GVfsBackendAfpBrowse * afp_backend,GAsyncResult * res,GError ** error)111 update_cache_finish (GVfsBackendAfpBrowse *afp_backend,
112                      GAsyncResult         *res,
113                      GError              **error)
114 {
115   g_return_val_if_fail (g_task_is_valid (res, afp_backend), FALSE);
116   g_return_val_if_fail (g_async_result_is_tagged (res, update_cache), FALSE);
117 
118   return g_task_propagate_boolean (G_TASK (res), error);
119 }
120 
121 static GVfsAfpVolumeData *
find_volume(GVfsBackendAfpBrowse * afp_backend,char * filename)122 find_volume (GVfsBackendAfpBrowse *afp_backend,
123              char *filename)
124 {
125   char *end;
126   guint len;
127   guint i;
128 
129   while (*filename == '/')
130     filename++;
131 
132   end = strchr (filename, '/');
133   if (end)
134   {
135     len = end - filename;
136 
137     while (*end == '/')
138       end++;
139 
140     if (*end != 0)
141       return NULL;
142   }
143   else
144     len = strlen (filename);
145 
146   for (i = 0; i < afp_backend->volumes->len; i++)
147   {
148     GVfsAfpVolumeData *vol_data = g_ptr_array_index (afp_backend->volumes, i);
149 
150     if (strlen (vol_data->name) == len && strncmp (vol_data->name, filename, len) == 0)
151       return vol_data;
152   }
153 
154   return NULL;
155 }
156 
157 static void
mount_mountable_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)158 mount_mountable_cb (GObject      *source_object,
159                     GAsyncResult *res,
160                     gpointer      user_data)
161 {
162   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
163   GVfsJobMountMountable *job = G_VFS_JOB_MOUNT_MOUNTABLE (user_data);
164 
165   GError *err;
166   GVfsAfpVolumeData *vol_data;
167   GMountSpec *mount_spec;
168 
169   if (!update_cache_finish (afp_backend, res, &err))
170   {
171     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
172     g_error_free (err);
173     return;
174   }
175 
176   vol_data = find_volume (afp_backend, job->filename);
177   if (!vol_data)
178   {
179     g_vfs_job_failed (G_VFS_JOB (job),  G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
180                       _("File doesn’t exist"));
181     return;
182   }
183 
184   mount_spec = g_mount_spec_new ("afp-volume");
185   g_mount_spec_set (mount_spec, "host",
186                     g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
187   g_mount_spec_set (mount_spec, "volume", vol_data->name);
188   g_mount_spec_set (mount_spec, "user", afp_backend->logged_in_user);
189 
190   g_vfs_job_mount_mountable_set_target (job, mount_spec, "/", TRUE);
191   g_mount_spec_unref (mount_spec);
192 
193   g_vfs_job_succeeded (G_VFS_JOB (job));
194 }
195 
196 static gboolean
try_mount_mountable(GVfsBackend * backend,GVfsJobMountMountable * job,const char * filename,GMountSource * mount_source)197 try_mount_mountable (GVfsBackend *backend,
198                      GVfsJobMountMountable *job,
199                      const char *filename,
200                      GMountSource *mount_source)
201 {
202   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
203 
204   if (is_root (filename))
205   {
206     g_vfs_job_failed (G_VFS_JOB (job),
207                       G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
208                       _("Not a mountable file"));
209     return TRUE;
210   }
211 
212   update_cache (afp_backend, G_VFS_JOB (job)->cancellable, mount_mountable_cb, job);
213 
214   return TRUE;
215 
216 }
217 
218 static void
fill_info(GFileInfo * info,GVfsAfpVolumeData * vol_data,GVfsBackendAfpBrowse * afp_backend)219 fill_info (GFileInfo *info, GVfsAfpVolumeData *vol_data, GVfsBackendAfpBrowse *afp_backend)
220 {
221   GIcon *icon;
222   GMountSpec *mount_spec;
223   char *uri;
224 
225   g_file_info_set_name (info, vol_data->name);
226   g_file_info_set_display_name (info, vol_data->name);
227   g_file_info_set_edit_name (info, vol_data->name);
228   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
229   g_file_info_set_content_type (info, "inode/directory");
230   g_file_info_set_file_type (info, G_FILE_TYPE_MOUNTABLE);
231 
232   g_file_info_set_attribute_boolean (info, "afp::volume-password-protected", (vol_data->flags & 0x01));
233 
234   icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-afp");
235   g_file_info_set_icon (info, icon);
236   g_object_unref (icon);
237 
238   icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-symbolic");
239   g_file_info_set_symbolic_icon (info, icon);
240   g_object_unref (icon);
241 
242   mount_spec = g_mount_spec_new ("afp-volume");
243   g_mount_spec_set (mount_spec, "host",
244                     g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
245   g_mount_spec_set (mount_spec, "volume", vol_data->name);
246   g_mount_spec_set (mount_spec, "user", afp_backend->logged_in_user);
247 
248   if (g_mount_tracker_has_mount_spec (afp_backend->mount_tracker, mount_spec))
249     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, FALSE);
250   else
251     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, TRUE);
252   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, FALSE);
253   g_mount_spec_unref (mount_spec);
254 
255   uri = g_strdup_printf ("afp://%s/%s",
256                          g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)),
257                          vol_data->name);
258   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
259                                     uri);
260   g_free (uri);
261 }
262 
263 static void
enumerate_cache_updated_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)264 enumerate_cache_updated_cb (GObject      *source_object,
265                             GAsyncResult *res,
266                             gpointer      user_data)
267 {
268   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
269   GVfsJobEnumerate *job = G_VFS_JOB_ENUMERATE (user_data);
270 
271   GError *err = NULL;
272   guint i;
273 
274   if (!update_cache_finish (afp_backend, res, &err))
275   {
276     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
277     g_error_free (err);
278     return;
279   }
280 
281   g_vfs_job_succeeded (G_VFS_JOB (job));
282 
283   for (i = 0; i < afp_backend->volumes->len; i++)
284   {
285     GVfsAfpVolumeData *vol_data = g_ptr_array_index (afp_backend->volumes, i);
286 
287     GFileInfo *info;
288 
289     info = g_file_info_new ();
290     fill_info (info, vol_data, afp_backend);
291     g_vfs_job_enumerate_add_info (job, info);
292 
293     g_object_unref (info);
294   }
295 
296   g_vfs_job_enumerate_done (job);
297 }
298 
299 static gboolean
try_enumerate(GVfsBackend * backend,GVfsJobEnumerate * job,const char * filename,GFileAttributeMatcher * attribute_matcher,GFileQueryInfoFlags flags)300 try_enumerate (GVfsBackend *backend,
301                GVfsJobEnumerate *job,
302                const char *filename,
303                GFileAttributeMatcher *attribute_matcher,
304                GFileQueryInfoFlags flags)
305 {
306   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
307 
308   if (!is_root(filename))
309   {
310     g_vfs_job_failed (G_VFS_JOB (job),
311                       G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
312                       _("File doesn’t exist"));
313     return TRUE;
314   }
315 
316   update_cache (afp_backend, G_VFS_JOB (job)->cancellable,
317                 enumerate_cache_updated_cb, job);
318 
319   return TRUE;
320 }
321 
322 static void
query_info_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)323 query_info_cb (GObject      *source_object,
324                GAsyncResult *res,
325                gpointer      user_data)
326 {
327   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (source_object);
328   GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
329 
330   GError *err = NULL;
331   GVfsAfpVolumeData *vol_data;
332 
333   if (!update_cache_finish (afp_backend, res, &err))
334   {
335     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
336     g_error_free (err);
337     return;
338   }
339 
340   vol_data = find_volume (afp_backend, job->filename);
341   if (!vol_data)
342   {
343     g_vfs_job_failed_literal (G_VFS_JOB (job),  G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
344                               _("File doesn’t exist"));
345     return;
346   }
347 
348   fill_info (job->file_info, vol_data, afp_backend);
349   g_vfs_job_succeeded (G_VFS_JOB (job));
350 }
351 
352 static gboolean
try_query_info(GVfsBackend * backend,GVfsJobQueryInfo * job,const char * filename,GFileQueryInfoFlags flags,GFileInfo * info,GFileAttributeMatcher * matcher)353 try_query_info (GVfsBackend *backend,
354                 GVfsJobQueryInfo *job,
355                 const char *filename,
356                 GFileQueryInfoFlags flags,
357                 GFileInfo *info,
358                 GFileAttributeMatcher *matcher)
359 {
360   if (is_root (filename))
361   {
362     GIcon *icon;
363 
364     g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
365     g_file_info_set_name (info, "/");
366     g_file_info_set_display_name (info, g_vfs_backend_get_display_name (backend));
367     g_file_info_set_content_type (info, "inode/directory");
368     icon = g_vfs_backend_get_icon (backend);
369     if (icon != NULL)
370       g_file_info_set_icon (info, icon);
371 
372     icon = g_vfs_backend_get_symbolic_icon (backend);
373     if (icon != NULL)
374       g_file_info_set_symbolic_icon (info, icon);
375 
376     g_vfs_job_succeeded (G_VFS_JOB (job));
377   }
378   else
379     update_cache (G_VFS_BACKEND_AFP_BROWSE (backend), G_VFS_JOB (job)->cancellable,
380                   query_info_cb, job);
381 
382   return TRUE;
383 }
384 
385 static void
do_unmount(GVfsBackend * backend,GVfsJobUnmount * job,GMountUnmountFlags flags,GMountSource * mount_source)386 do_unmount (GVfsBackend *backend,
387             GVfsJobUnmount *job,
388             GMountUnmountFlags flags,
389             GMountSource *mount_source)
390 {
391   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
392 
393   if (!(flags & G_MOUNT_UNMOUNT_FORCE))
394   {
395     g_vfs_afp_server_logout_sync (afp_backend->server, G_VFS_JOB (job)->cancellable,
396                                   NULL);
397   }
398 
399   g_vfs_job_succeeded (G_VFS_JOB (job));
400 }
401 
402 static void
do_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)403 do_mount (GVfsBackend *backend,
404           GVfsJobMount *job,
405           GMountSpec *mount_spec,
406           GMountSource *mount_source,
407           gboolean is_automount)
408 {
409   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
410 
411   gboolean res;
412   GError *err = NULL;
413 
414   const GVfsAfpServerInfo *info;
415   GMountSpec *afp_mount_spec;
416   char       *server_name;
417   char       *display_name;
418 
419   afp_backend->server = g_vfs_afp_server_new (afp_backend->addr);
420 
421   res = g_vfs_afp_server_login (afp_backend->server, afp_backend->user, mount_source,
422                                 &afp_backend->logged_in_user,
423                                 G_VFS_JOB (job)->cancellable, &err);
424   if (!res)
425     goto error;
426 
427   /* set mount info */
428   afp_mount_spec = g_mount_spec_new ("afp-server");
429   g_mount_spec_set (afp_mount_spec, "host",
430                     g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
431   if (afp_backend->user)
432     g_mount_spec_set (afp_mount_spec, "user", afp_backend->user);
433 
434   g_vfs_backend_set_mount_spec (backend, afp_mount_spec);
435   g_mount_spec_unref (afp_mount_spec);
436 
437   info = g_vfs_afp_server_get_info (afp_backend->server);
438 
439   if (info->utf8_server_name)
440     server_name = info->utf8_server_name;
441   else
442     server_name = info->server_name;
443 
444   if (afp_backend->user)
445     /* Translators: first %s is username and second serververname */
446     display_name = g_strdup_printf (_("%s on %s"), afp_backend->user,
447                                     server_name);
448   else
449     /* Translators: %s is the servername */
450     display_name = g_strdup_printf (_("%s"),
451                                     server_name);
452   g_vfs_backend_set_display_name (backend, display_name);
453   g_free (display_name);
454 
455   g_vfs_backend_set_icon_name (backend, "network-server-afp");
456   g_vfs_backend_set_user_visible (backend, FALSE);
457 
458 
459   g_vfs_job_succeeded (G_VFS_JOB (job));
460   return;
461 
462 error:
463   g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
464 }
465 
466 static gboolean
try_mount(GVfsBackend * backend,GVfsJobMount * job,GMountSpec * mount_spec,GMountSource * mount_source,gboolean is_automount)467 try_mount (GVfsBackend *backend,
468            GVfsJobMount *job,
469            GMountSpec *mount_spec,
470            GMountSource *mount_source,
471            gboolean is_automount)
472 {
473   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (backend);
474 
475   const char *host, *portstr, *user;
476   guint16 port = 548;
477 
478   host = g_mount_spec_get (mount_spec, "host");
479   if (host == NULL)
480   {
481     g_vfs_job_failed (G_VFS_JOB (job),
482                       G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
483                       _("No hostname specified"));
484     return TRUE;
485   }
486 
487   portstr = g_mount_spec_get (mount_spec, "port");
488   if (portstr != NULL)
489   {
490     port = atoi (portstr);
491   }
492 
493   afp_backend->addr = G_NETWORK_ADDRESS (g_network_address_new (host, port));
494 
495   user = g_mount_spec_get (mount_spec, "user");
496   afp_backend->user = g_strdup (user);
497 
498   return FALSE;
499 }
500 
501 static void
g_vfs_backend_afp_browse_init(GVfsBackendAfpBrowse * object)502 g_vfs_backend_afp_browse_init (GVfsBackendAfpBrowse *object)
503 {
504   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (object);
505 
506   afp_backend->mount_tracker = g_mount_tracker_new (NULL, FALSE);
507 
508   afp_backend->addr = NULL;
509   afp_backend->user = NULL;
510 
511   afp_backend->logged_in_user = NULL;
512   afp_backend->volumes = NULL;
513 }
514 
515 static void
g_vfs_backend_afp_browse_finalize(GObject * object)516 g_vfs_backend_afp_browse_finalize (GObject *object)
517 {
518   GVfsBackendAfpBrowse *afp_backend = G_VFS_BACKEND_AFP_BROWSE (object);
519 
520   g_object_unref (afp_backend->mount_tracker);
521 
522   if (afp_backend->addr)
523     g_object_unref (afp_backend->addr);
524 
525   g_free (afp_backend->user);
526 
527   g_free (afp_backend->logged_in_user);
528   if (afp_backend->volumes)
529     g_ptr_array_unref (afp_backend->volumes);
530 
531   G_OBJECT_CLASS (g_vfs_backend_afp_browse_parent_class)->finalize (object);
532 }
533 
534 static gboolean
try_query_fs_info(GVfsBackend * backend,GVfsJobQueryFsInfo * job,const char * filename,GFileInfo * info,GFileAttributeMatcher * matcher)535 try_query_fs_info (GVfsBackend *backend,
536                    GVfsJobQueryFsInfo *job,
537                    const char *filename,
538                    GFileInfo *info,
539                    GFileAttributeMatcher *matcher)
540 {
541   g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
542   g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
543   g_vfs_job_succeeded (G_VFS_JOB (job));
544   return TRUE;
545 }
546 
547 static void
g_vfs_backend_afp_browse_class_init(GVfsBackendAfpBrowseClass * klass)548 g_vfs_backend_afp_browse_class_init (GVfsBackendAfpBrowseClass *klass)
549 {
550   GObjectClass *object_class = G_OBJECT_CLASS (klass);
551   GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
552 
553   object_class->finalize = g_vfs_backend_afp_browse_finalize;
554 
555   backend_class->try_mount = try_mount;
556   backend_class->mount = do_mount;
557   backend_class->unmount = do_unmount;
558   backend_class->try_query_info = try_query_info;
559   backend_class->try_enumerate = try_enumerate;
560   backend_class->try_mount_mountable = try_mount_mountable;
561   backend_class->try_query_fs_info = try_query_fs_info;
562 }
563 
564 void
g_vfs_afp_browse_daemon_init(void)565 g_vfs_afp_browse_daemon_init (void)
566 {
567   g_set_application_name (_("Apple Filing Protocol Service"));
568 
569 #ifdef HAVE_GCRYPT
570   gcry_check_version (NULL);
571   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
572 #endif
573 }
574