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