1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <glib/gi18n-lib.h>
6
7 #include <string.h>
8 #include <time.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11
12 #include "shares.h"
13
14 #undef DEBUG_SHARES
15 #ifdef DEBUG_SHARES
16 # define NET_USERSHARE_ARGV0 "debug-net-usershare"
17 #else
18 # define NET_USERSHARE_ARGV0 "net"
19 #endif
20
21 static GHashTable *path_share_info_hash;
22 static GHashTable *share_name_share_info_hash;
23
24 #define NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES 100
25 #define TIMESTAMP_THRESHOLD 10 /* seconds */
26 static int refresh_timestamp_update_counter;
27 static time_t refresh_timestamp;
28
29 #define KEY_PATH "path"
30 #define KEY_COMMENT "comment"
31 #define KEY_ACL "usershare_acl"
32 #define KEY_GUEST_OK "guest_ok"
33
34
35 /* Debugging flags */
36 static gboolean throw_error_on_refresh;
37 static gboolean throw_error_on_add;
38 static gboolean throw_error_on_modify;
39 static gboolean throw_error_on_remove;
40
41 /* Interface to "net usershare" */
42
43 static gboolean
net_usershare_run(int argc,char ** argv,GKeyFile ** ret_key_file,GError ** error)44 net_usershare_run (int argc, char **argv, GKeyFile **ret_key_file, GError **error)
45 {
46 int real_argc;
47 int i;
48 char **real_argv;
49 gboolean retval;
50 char *stdout_contents;
51 char *stderr_contents;
52 int exit_status;
53 int exit_code;
54 GKeyFile *key_file;
55 GError *real_error;
56
57 g_assert (argc > 0);
58 g_assert (argv != NULL);
59 g_assert (error == NULL || *error == NULL);
60
61 if (ret_key_file)
62 *ret_key_file = NULL;
63
64 /* Build command line */
65
66 real_argc = 2 + argc + 1; /* "net" "usershare" [argv] NULL */
67 real_argv = g_new (char *, real_argc);
68
69 real_argv[0] = NET_USERSHARE_ARGV0;
70 real_argv[1] = "usershare";
71
72 for (i = 0; i < argc; i++) {
73 g_assert (argv[i] != NULL);
74 real_argv[i + 2] = argv[i];
75 }
76
77 real_argv[real_argc - 1] = NULL;
78
79 /* Launch */
80
81 stdout_contents = NULL;
82 stderr_contents = NULL;
83 /*
84 {
85 char **p;
86
87 g_message ("------------------------------------------");
88
89 for (p = real_argv; *p; p++)
90 g_message ("spawn arg \"%s\"", *p);
91
92 g_message ("end of spawn args; SPAWNING\n");
93 }
94 */
95 real_error = NULL;
96 retval = g_spawn_sync (NULL, /* cwd */
97 real_argv,
98 NULL, /* envp */
99 G_SPAWN_SEARCH_PATH,
100 NULL, /* GSpawnChildSetupFunc */
101 NULL, /* user_data */
102 &stdout_contents,
103 &stderr_contents,
104 &exit_status,
105 &real_error);
106
107 /* g_message ("returned from spawn: %s: %s", retval ? "SUCCESS" : "FAIL", retval ? "" : real_error->message); */
108
109 if (!retval) {
110 g_propagate_error (error, real_error);
111 goto out;
112 }
113
114 if (!WIFEXITED (exit_status)) {
115 g_message ("WIFEXITED(%d) was false!", exit_status);
116 retval = FALSE;
117
118 if (WIFSIGNALED (exit_status)) {
119 int signal_num;
120
121 signal_num = WTERMSIG (exit_status);
122 g_message ("Child got signal %d", signal_num);
123
124 g_set_error (error,
125 SHARES_ERROR,
126 SHARES_ERROR_FAILED,
127 _("%s %s %s returned with signal %d"),
128 real_argv[0],
129 real_argv[1],
130 real_argv[2],
131 signal_num);
132 } else
133 g_set_error (error,
134 SHARES_ERROR,
135 SHARES_ERROR_FAILED,
136 _("%s %s %s failed for an unknown reason"),
137 real_argv[0],
138 real_argv[1],
139 real_argv[2]);
140
141 goto out;
142 }
143
144 exit_code = WEXITSTATUS (exit_status);
145
146 /* g_message ("exit code %d", exit_code); */
147 if (exit_code != 0) {
148 char *str;
149 char *message;
150
151 /* stderr_contents is in the system locale encoding, not UTF-8 */
152
153 str = g_locale_to_utf8 (stderr_contents, -1, NULL, NULL, NULL);
154
155 if (str && str[0])
156 message = g_strdup_printf (_("'net usershare' returned error %d: %s"), exit_code, str);
157 else
158 message = g_strdup_printf (_("'net usershare' returned error %d"), exit_code);
159
160 g_free (str);
161
162 g_set_error (error,
163 G_SPAWN_ERROR,
164 G_SPAWN_ERROR_FAILED,
165 "%s",
166 message);
167
168 g_free (message);
169
170 retval = FALSE;
171 goto out;
172 }
173
174 if (ret_key_file) {
175 /* g_message ("caller wants GKeyFile"); */
176
177 *ret_key_file = NULL;
178
179 /* FIXME: jeallison@novell.com says the output of "net usershare" is nearly always
180 * in UTF-8, but that it can be configured in the master smb.conf. We assume
181 * UTF-8 for now.
182 */
183
184 if (!g_utf8_validate (stdout_contents, -1, NULL)) {
185 g_message ("stdout of net usershare was not in valid UTF-8");
186 g_set_error (error,
187 G_SPAWN_ERROR,
188 G_SPAWN_ERROR_FAILED,
189 _("the output of 'net usershare' is not in valid UTF-8 encoding"));
190 retval = FALSE;
191 goto out;
192 }
193
194 key_file = g_key_file_new ();
195
196 real_error = NULL;
197 if (!g_key_file_load_from_data (key_file, stdout_contents, -1, 0, &real_error)) {
198 g_message ("Error when parsing key file {\n%s\n}: %s", stdout_contents, real_error->message);
199 g_propagate_error (error, real_error);
200 g_key_file_free (key_file);
201 retval = FALSE;
202 goto out;
203 }
204
205 retval = TRUE;
206 *ret_key_file = key_file;
207 } else
208 retval = TRUE;
209
210 /* g_message ("success from calling net usershare and parsing its output"); */
211
212 out:
213 g_free (real_argv);
214 g_free (stdout_contents);
215 g_free (stderr_contents);
216
217 /* g_message ("------------------------------------------"); */
218
219 return retval;
220 }
221
222
223 /* Internals */
224
225 static void
ensure_hashes(void)226 ensure_hashes (void)
227 {
228 if (path_share_info_hash == NULL) {
229 g_assert (share_name_share_info_hash == NULL);
230
231 path_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal);
232 share_name_share_info_hash = g_hash_table_new (g_str_hash, g_str_equal);
233 } else
234 g_assert (share_name_share_info_hash != NULL);
235 }
236
237 static ShareInfo *
lookup_share_by_path(const char * path)238 lookup_share_by_path (const char *path)
239 {
240 ensure_hashes ();
241 return g_hash_table_lookup (path_share_info_hash, path);
242 }
243
244 static ShareInfo *
lookup_share_by_share_name(const char * share_name)245 lookup_share_by_share_name (const char *share_name)
246 {
247 ensure_hashes ();
248 return g_hash_table_lookup (share_name_share_info_hash, share_name);
249 }
250
251 static void
add_share_info_to_hashes(ShareInfo * info)252 add_share_info_to_hashes (ShareInfo *info)
253 {
254 ensure_hashes ();
255 g_hash_table_insert (path_share_info_hash, info->path, info);
256 g_hash_table_insert (share_name_share_info_hash, info->share_name, info);
257 }
258
259 static void
remove_share_info_from_hashes(ShareInfo * info)260 remove_share_info_from_hashes (ShareInfo *info)
261 {
262 ensure_hashes ();
263 g_hash_table_remove (path_share_info_hash, info->path);
264 g_hash_table_remove (share_name_share_info_hash, info->share_name);
265 }
266
267 static gboolean
remove_from_path_hash_cb(gpointer key,gpointer value,gpointer data)268 remove_from_path_hash_cb (gpointer key,
269 gpointer value,
270 gpointer data)
271 {
272 ShareInfo *info;
273
274 info = value;
275 shares_free_share_info (info);
276
277 return TRUE;
278 }
279
280 static gboolean
remove_from_share_name_hash_cb(gpointer key,gpointer value,gpointer data)281 remove_from_share_name_hash_cb (gpointer key,
282 gpointer value,
283 gpointer data)
284 {
285 /* The ShareInfo was already freed in remove_from_path_hash_cb() */
286 return TRUE;
287 }
288
289 static void
free_all_shares(void)290 free_all_shares (void)
291 {
292 ensure_hashes ();
293 g_hash_table_foreach_remove (path_share_info_hash, remove_from_path_hash_cb, NULL);
294 g_hash_table_foreach_remove (share_name_share_info_hash, remove_from_share_name_hash_cb, NULL);
295 }
296
297 static char *
get_string_from_key_file(GKeyFile * key_file,const char * group,const char * key)298 get_string_from_key_file (GKeyFile *key_file, const char *group, const char *key)
299 {
300 GError *error;
301 char *str;
302
303 error = NULL;
304 str = NULL;
305
306 if (g_key_file_has_key (key_file, group, key, &error)) {
307 str = g_key_file_get_string (key_file, group, key, &error);
308 if (!str) {
309 g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)
310 && !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND));
311
312 g_error_free (error);
313 }
314 } else {
315 g_assert (!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND));
316 g_error_free (error);
317 }
318
319 return str;
320 }
321
322 static void
add_key_group_to_hashes(GKeyFile * key_file,const char * group)323 add_key_group_to_hashes (GKeyFile *key_file, const char *group)
324 {
325 char *path;
326 char *comment;
327 char *acl;
328 gboolean is_writable;
329 char *guest_ok_str;
330 gboolean guest_ok;
331 ShareInfo *info;
332 ShareInfo *old_info;
333
334 /* Remove the old share based on the name */
335
336 old_info = lookup_share_by_share_name (group);
337 if (old_info) {
338 remove_share_info_from_hashes (old_info);
339 shares_free_share_info (old_info);
340 }
341
342 /* Start parsing, and remove the old share based on the path */
343
344 path = get_string_from_key_file (key_file, group, KEY_PATH);
345 if (!path) {
346 g_message ("group '%s' doesn't have a '%s' key! Ignoring group.", group, KEY_PATH);
347 return;
348 }
349
350 old_info = lookup_share_by_path (path);
351 if (old_info) {
352 remove_share_info_from_hashes (old_info);
353 shares_free_share_info (old_info);
354 }
355
356 /* Finish parsing */
357
358 comment = get_string_from_key_file (key_file, group, KEY_COMMENT);
359
360 acl = get_string_from_key_file (key_file, group, KEY_ACL);
361 if (acl) {
362 if (strstr (acl, "Everyone:R"))
363 is_writable = FALSE;
364 else if (strstr (acl, "Everyone:F"))
365 is_writable = TRUE;
366 else {
367 g_message ("unknown format for key '%s/%s' as it contains '%s'. Assuming that the share is read-only",
368 group, KEY_ACL, acl);
369 is_writable = FALSE;
370 }
371
372 g_free (acl);
373 } else {
374 g_message ("group '%s' doesn't have a '%s' key! Assuming that the share is read-only.", group, KEY_ACL);
375 is_writable = FALSE;
376 }
377
378 guest_ok_str = get_string_from_key_file (key_file, group, KEY_GUEST_OK);
379 if (guest_ok_str) {
380 if (strcmp (guest_ok_str, "n") == 0)
381 guest_ok = FALSE;
382 else if (strcmp (guest_ok_str, "y") == 0)
383 guest_ok = TRUE;
384 else {
385 g_message ("unknown format for key '%s/%s' as it contains '%s'. Assuming that the share is not guest accessible.",
386 group, KEY_GUEST_OK, guest_ok_str);
387 guest_ok = FALSE;
388 }
389
390 g_free (guest_ok_str);
391 } else {
392 g_message ("group '%s' doesn't have a '%s' key! Assuming that the share is not guest accessible.", group, KEY_GUEST_OK);
393 guest_ok = FALSE;
394 }
395
396 g_assert (path != NULL);
397 g_assert (group != NULL);
398
399 info = g_new (ShareInfo, 1);
400 info->path = path;
401 info->share_name = g_strdup (group);
402 info->comment = comment;
403 info->is_writable = is_writable;
404 info->guest_ok = guest_ok;
405
406 add_share_info_to_hashes (info);
407 }
408
409 static void
replace_shares_from_key_file(GKeyFile * key_file)410 replace_shares_from_key_file (GKeyFile *key_file)
411 {
412 gsize num_groups;
413 char **group_names;
414 gsize i;
415
416 group_names = g_key_file_get_groups (key_file, &num_groups);
417
418 /* FIXME: In add_key_group_to_hashes(), we simply ignore key groups
419 * which have invalid data (i.e. no path). We could probably accumulate a
420 * GError with the list of invalid groups and propagate it upwards.
421 */
422 for (i = 0; i < num_groups; i++) {
423 g_assert (group_names[i] != NULL);
424 add_key_group_to_hashes (key_file, group_names[i]);
425 }
426
427 g_strfreev (group_names);
428 }
429
430 static gboolean
refresh_shares(GError ** error)431 refresh_shares (GError **error)
432 {
433 GKeyFile *key_file;
434 char *argv[1];
435 GError *real_error;
436
437 free_all_shares ();
438
439 if (throw_error_on_refresh) {
440 g_set_error (error,
441 SHARES_ERROR,
442 SHARES_ERROR_FAILED,
443 _("Failed"));
444 return FALSE;
445 }
446
447 argv[0] = "info";
448
449 real_error = NULL;
450 if (!net_usershare_run (G_N_ELEMENTS (argv), argv, &key_file, &real_error)) {
451 g_message ("Called \"net usershare info\" but it failed: %s", real_error->message);
452 g_propagate_error (error, real_error);
453 return FALSE;
454 }
455
456 g_assert (key_file != NULL);
457
458 replace_shares_from_key_file (key_file);
459 g_key_file_free (key_file);
460
461 return TRUE;
462 }
463
464 static gboolean
refresh_if_needed(GError ** error)465 refresh_if_needed (GError **error)
466 {
467 gboolean retval;
468
469 if (refresh_timestamp_update_counter == 0) {
470 time_t new_timestamp;
471
472 refresh_timestamp_update_counter = NUM_CALLS_BETWEEN_TIMESTAMP_UPDATES;
473
474 new_timestamp = time (NULL);
475 if (new_timestamp - refresh_timestamp > TIMESTAMP_THRESHOLD) {
476 /* g_message ("REFRESHING SHARES"); */
477 retval = refresh_shares (error);
478 } else
479 retval = TRUE;
480
481 refresh_timestamp = new_timestamp;
482 } else {
483 refresh_timestamp_update_counter--;
484 retval = TRUE;
485 }
486
487 return retval;
488 }
489
490 static ShareInfo *
copy_share_info(ShareInfo * info)491 copy_share_info (ShareInfo *info)
492 {
493 ShareInfo *copy;
494
495 if (!info)
496 return NULL;
497
498 copy = g_new (ShareInfo, 1);
499 copy->path = g_strdup (info->path);
500 copy->share_name = g_strdup (info->share_name);
501 copy->comment = g_strdup (info->comment);
502 copy->is_writable = info->is_writable;
503 copy->guest_ok = info->guest_ok;
504
505 return copy;
506 }
507
508 /**
509 * shares_supports_guest_ok:
510 * @supports_guest_ok_ret: Location to store whether "usershare allow guests"
511 * is enabled.
512 * @error: Location to store error, or #NULL.
513 *
514 * Determines whether the option "usershare allow guests" is enabled in samba
515 * config as shown by testparm.
516 *
517 * Return value: #TRUE if if the info could be queried successfully, #FALSE
518 * otherwise. If this function returns #FALSE, an error code will be returned
519 * in the @error argument, and *@ret_info_list will be set to #FALSE.
520 **/
521 gboolean
shares_supports_guest_ok(gboolean * supports_guest_ok_ret,GError ** error)522 shares_supports_guest_ok (gboolean *supports_guest_ok_ret, GError **error)
523 {
524 gboolean retval;
525 gboolean result;
526 char *stdout_contents;
527 char *stderr_contents;
528 int exit_status;
529 int exit_code;
530
531 *supports_guest_ok_ret = FALSE;
532
533 result = g_spawn_command_line_sync ("testparm -s --parameter-name='usershare allow guests'",
534 &stdout_contents,
535 &stderr_contents,
536 &exit_status,
537 error);
538 if (!result)
539 return FALSE;
540
541 retval = FALSE;
542
543 if (!WIFEXITED (exit_status)) {
544 if (WIFSIGNALED (exit_status)) {
545 int signal_num;
546
547 signal_num = WTERMSIG (exit_status);
548 g_set_error (error,
549 SHARES_ERROR,
550 SHARES_ERROR_FAILED,
551 _("Samba's testparm returned with signal %d"),
552 signal_num);
553 } else
554 g_set_error (error,
555 SHARES_ERROR,
556 SHARES_ERROR_FAILED,
557 _("Samba's testparm failed for an unknown reason"));
558
559 goto out;
560 }
561
562 exit_code = WEXITSTATUS (exit_status);
563 if (exit_code != 0) {
564 char *str;
565 char *message;
566
567 /* stderr_contents is in the system locale encoding, not UTF-8 */
568
569 str = g_locale_to_utf8 (stderr_contents, -1, NULL, NULL, NULL);
570
571 if (str && str[0])
572 message = g_strdup_printf (_("Samba's testparm returned error %d: %s"), exit_code, str);
573 else
574 message = g_strdup_printf (_("Samba's testparm returned error %d"), exit_code);
575
576 g_free (str);
577
578 g_set_error (error,
579 G_SPAWN_ERROR,
580 G_SPAWN_ERROR_FAILED,
581 "%s",
582 message);
583
584 g_free (message);
585
586 goto out;
587 }
588
589 retval = TRUE;
590 *supports_guest_ok_ret = (g_ascii_strncasecmp (stdout_contents, "Yes", 3) == 0);
591
592 out:
593 g_free (stdout_contents);
594 g_free (stderr_contents);
595
596 return retval;
597 }
598
599 static gboolean
add_share(ShareInfo * info,GError ** error)600 add_share (ShareInfo *info, GError **error)
601 {
602 char *argv[7];
603 int argc;
604 ShareInfo *copy;
605 GKeyFile *key_file;
606 GError *real_error;
607 gboolean supports_success;
608 gboolean supports_guest_ok;
609 gboolean net_usershare_success;
610
611 /* g_message ("add_share() start"); */
612
613 if (throw_error_on_add) {
614 g_set_error (error,
615 SHARES_ERROR,
616 SHARES_ERROR_FAILED,
617 _("Failed"));
618 g_message ("add_share() end FAIL");
619 return FALSE;
620 }
621
622 supports_success = shares_supports_guest_ok (&supports_guest_ok, error);
623 if (!supports_success)
624 return FALSE;
625
626 argv[0] = "add";
627 argv[1] = "-l";
628 argv[2] = info->share_name;
629 argv[3] = info->path;
630 argv[4] = info->comment;
631 argv[5] = info->is_writable ? "Everyone:F" : g_strdup_printf ("Everyone:R,%s:F", g_get_user_name ());
632
633 if (supports_guest_ok) {
634 argv[6] = info->guest_ok ? "guest_ok=y" : "guest_ok=n";
635 argc = 7;
636 } else
637 argc = 6;
638
639 real_error = NULL;
640 net_usershare_success = net_usershare_run (argc, argv, &key_file, &real_error);
641 if (!info->is_writable) g_free (argv[5]);
642
643 if (!net_usershare_success) {
644 g_message ("Called \"net usershare add\" but it failed: %s", real_error->message);
645 g_propagate_error (error, real_error);
646 return FALSE;
647 }
648
649 replace_shares_from_key_file (key_file);
650
651 copy = copy_share_info (info);
652 add_share_info_to_hashes (copy);
653
654 /* g_message ("add_share() end SUCCESS"); */
655
656 return TRUE;
657 }
658
659 static gboolean
remove_share(const char * path,GError ** error)660 remove_share (const char *path, GError **error)
661 {
662 ShareInfo *old_info;
663 char *argv[2];
664 GError *real_error;
665
666 /* g_message ("remove_share() start"); */
667
668 if (throw_error_on_remove) {
669 g_set_error (error,
670 SHARES_ERROR,
671 SHARES_ERROR_FAILED,
672 "Failed");
673 g_message ("remove_share() end FAIL");
674 return FALSE;
675 }
676
677 old_info = lookup_share_by_path (path);
678 if (!old_info) {
679 char *display_name;
680
681 display_name = g_filename_display_name (path);
682 g_set_error (error,
683 SHARES_ERROR,
684 SHARES_ERROR_NONEXISTENT,
685 _("Cannot remove the share for path %s: that path is not shared"),
686 display_name);
687 g_free (display_name);
688
689 g_message ("remove_share() end FAIL: path %s was not in our hashes", path);
690 return FALSE;
691 }
692
693 argv[0] = "delete";
694 argv[1] = old_info->share_name;
695
696 real_error = NULL;
697 if (!net_usershare_run (G_N_ELEMENTS (argv), argv, NULL, &real_error)) {
698 g_message ("Called \"net usershare delete\" but it failed: %s", real_error->message);
699 g_propagate_error (error, real_error);
700 g_message ("remove_share() end FAIL");
701 return FALSE;
702 }
703
704 remove_share_info_from_hashes (old_info);
705 shares_free_share_info (old_info);
706
707 /* g_message ("remove_share() end SUCCESS"); */
708
709 return TRUE;
710 }
711
712 static gboolean
modify_share(const char * old_path,ShareInfo * info,GError ** error)713 modify_share (const char *old_path, ShareInfo *info, GError **error)
714 {
715 ShareInfo *old_info;
716
717 /* g_message ("modify_share() start"); */
718
719 old_info = lookup_share_by_path (old_path);
720 if (old_info == NULL) {
721 /*g_message ("modify_share() end; calling add_share() instead");*/
722 return add_share (info, error);
723 }
724
725 g_assert (old_info != NULL);
726
727 if (strcmp (info->path, old_info->path) != 0) {
728 g_set_error (error,
729 SHARES_ERROR,
730 SHARES_ERROR_FAILED,
731 _("Cannot change the path of an existing share; please remove the old share first and add a new one"));
732 g_message ("modify_share() end FAIL: tried to change the path in a share!");
733 return FALSE;
734 }
735
736 if (throw_error_on_modify) {
737 g_set_error (error,
738 SHARES_ERROR,
739 SHARES_ERROR_FAILED,
740 "Failed");
741 g_message ("modify_share() end FAIL");
742 return FALSE;
743 }
744
745 /* Although "net usershare add" will modify an existing share if it has the same share name
746 * as the one that gets passed in, our semantics are different. We have a one-to-one mapping
747 * between paths and share names; "net usershare" supports a one-to-many mapping from paths
748 * to share names. So, we must first remove the old share and then add the new/modified one.
749 */
750
751 if (!remove_share (old_path, error)) {
752 g_message ("modify_share() end FAIL: error when removing old share");
753 return FALSE;
754 }
755
756 /* g_message ("modify_share() end: will call add_share() with the new share info"); */
757 return add_share (info, error);
758 }
759
760
761 /* Public API */
762
763 GQuark
shares_error_quark(void)764 shares_error_quark (void)
765 {
766 static GQuark quark;
767
768 if (quark == 0)
769 quark = g_quark_from_string ("caja-share-error-quark"); /* not from_static_string since we are a module */
770
771 return quark;
772 }
773
774 /**
775 * shares_free_share_info:
776 * @info: A #ShareInfo structure.
777 *
778 * Frees a #ShareInfo structure.
779 **/
780 void
shares_free_share_info(ShareInfo * info)781 shares_free_share_info (ShareInfo *info)
782 {
783 g_assert (info != NULL);
784
785 g_free (info->path);
786 g_free (info->share_name);
787 g_free (info->comment);
788 g_free (info);
789 }
790
791 /**
792 * shares_get_path_is_shared:
793 * @path: A full path name ("/foo/bar/baz") in file system encoding.
794 * @ret_is_shared: Location to store result value (#TRUE if the path is shared, #FALSE otherwise)
795 * @error: Location to store error, or #NULL.
796 *
797 * Checks whether a path is shared through Samba.
798 *
799 * Return value: #TRUE if the info could be queried successfully, #FALSE
800 * otherwise. If this function returns #FALSE, an error code will be returned in the
801 * @error argument, and *@ret_is_shared will be set to #FALSE.
802 **/
803 gboolean
shares_get_path_is_shared(const char * path,gboolean * ret_is_shared,GError ** error)804 shares_get_path_is_shared (const char *path, gboolean *ret_is_shared, GError **error)
805 {
806 g_assert (ret_is_shared != NULL);
807 g_assert (error == NULL || *error == NULL);
808
809 if (!refresh_if_needed (error)) {
810 *ret_is_shared = FALSE;
811 return FALSE;
812 }
813
814 *ret_is_shared = (lookup_share_by_path (path) != NULL);
815
816 return TRUE;
817 }
818
819 /**
820 * shares_get_share_info_for_path:
821 * @path: A full path name ("/foo/bar/baz") in file system encoding.
822 * @ret_share_info: Location to store result with the share's info - on return,
823 * will be non-NULL if the path is indeed shared, or #NULL if the path is not
824 * shared. You must free the non-NULL value with shares_free_share_info().
825 * @error: Location to store error, or #NULL.
826 *
827 * Queries the information for a shared path: its share name, its read-only status, etc.
828 *
829 * Return value: #TRUE if the info could be queried successfully, #FALSE
830 * otherwise. If this function returns #FALSE, an error code will be returned in the
831 * @error argument, and *@ret_share_info will be set to #NULL.
832 **/
833 gboolean
shares_get_share_info_for_path(const char * path,ShareInfo ** ret_share_info,GError ** error)834 shares_get_share_info_for_path (const char *path, ShareInfo **ret_share_info, GError **error)
835 {
836 ShareInfo *info;
837
838 g_assert (path != NULL);
839 g_assert (ret_share_info != NULL);
840 g_assert (error == NULL || *error == NULL);
841
842 if (!refresh_if_needed (error)) {
843 *ret_share_info = NULL;
844 return FALSE;
845 }
846
847 info = lookup_share_by_path (path);
848 *ret_share_info = copy_share_info (info);
849
850 return TRUE;
851 }
852
853 /**
854 * shares_get_share_name_exists:
855 * @share_name: Name of a share.
856 * @ret_exists: Location to store return value; #TRUE if the share name exists, #FALSE otherwise.
857 *
858 * Queries whether a share name already exists in the user's list of shares.
859 *
860 * Return value: #TRUE if the info could be queried successfully, #FALSE
861 * otherwise. If this function returns #FALSE, an error code will be returned in the
862 * @error argument, and *@ret_exists will be set to #FALSE.
863 **/
864 gboolean
shares_get_share_name_exists(const char * share_name,gboolean * ret_exists,GError ** error)865 shares_get_share_name_exists (const char *share_name, gboolean *ret_exists, GError **error)
866 {
867 g_assert (share_name != NULL);
868 g_assert (ret_exists != NULL);
869 g_assert (error == NULL || *error == NULL);
870
871 if (!refresh_if_needed (error)) {
872 *ret_exists = FALSE;
873 return FALSE;
874 }
875
876 *ret_exists = (lookup_share_by_share_name (share_name) != NULL);
877
878 return TRUE;
879 }
880
881 /**
882 * shares_get_share_info_for_share_name:
883 * @share_name: Name of a share.
884 * @ret_share_info: Location to store result with the share's info - on return,
885 * will be non-NULL if there is a share for the specified name, or #NULL if no
886 * share has such name. You must free the non-NULL value with
887 * shares_free_share_info().
888 * @error: Location to store error, or #NULL.
889 *
890 * Queries the information for the share which has a specific name.
891 *
892 * Return value: #TRUE if the info could be queried successfully, #FALSE
893 * otherwise. If this function returns #FALSE, an error code will be returned in the
894 * @error argument, and *@ret_share_info will be set to #NULL.
895 **/
896 gboolean
shares_get_share_info_for_share_name(const char * share_name,ShareInfo ** ret_share_info,GError ** error)897 shares_get_share_info_for_share_name (const char *share_name, ShareInfo **ret_share_info, GError **error)
898 {
899 ShareInfo *info;
900
901 g_assert (share_name != NULL);
902 g_assert (ret_share_info != NULL);
903 g_assert (error == NULL || *error == NULL);
904
905 if (!refresh_if_needed (error)) {
906 *ret_share_info = NULL;
907 return FALSE;
908 }
909
910 info = lookup_share_by_share_name (share_name);
911 *ret_share_info = copy_share_info (info);
912
913 return TRUE;
914 }
915
916 /**
917 * shares_modify_share:
918 * @old_path: Path of the share to modify, or %NULL.
919 * @info: Info of the share to modify/add, or %NULL to delete a share.
920 * @error: Location to store error, or #NULL.
921 *
922 * Can add, modify, or delete shares. To add a share, pass %NULL for @old_path,
923 * and a non-null @info. To modify a share, pass a non-null @old_path and
924 * non-null @info; in this case, @info->path must have the same contents as
925 * @old_path. To remove a share, pass a non-NULL @old_path and a %NULL @info.
926 *
927 * Return value: TRUE if the share could be modified, FALSE otherwise. If this returns
928 * FALSE, then the error information will be placed in @error.
929 **/
930 gboolean
shares_modify_share(const char * old_path,ShareInfo * info,GError ** error)931 shares_modify_share (const char *old_path, ShareInfo *info, GError **error)
932 {
933 g_assert ((old_path == NULL && info != NULL)
934 || (old_path != NULL && info == NULL)
935 || (old_path != NULL && info != NULL));
936 g_assert (error == NULL || *error == NULL);
937
938 if (!refresh_if_needed (error))
939 return FALSE;
940
941 if (old_path == NULL)
942 return add_share (info, error);
943 else if (info == NULL)
944 return remove_share (old_path, error);
945 else
946 return modify_share (old_path, info, error);
947 }
948
949 static void
copy_to_slist_cb(gpointer key,gpointer value,gpointer data)950 copy_to_slist_cb (gpointer key, gpointer value, gpointer data)
951 {
952 ShareInfo *info;
953 ShareInfo *copy;
954 GSList **list;
955
956 info = value;
957 list = data;
958
959 copy = copy_share_info (info);
960 *list = g_slist_prepend (*list, copy);
961 }
962
963 /**
964 * shares_get_share_info_list:
965 * @ret_info_list: Location to store the return value, which is a list
966 * of #ShareInfo structures. Free this with shares_free_share_info_list().
967 * @error: Location to store error, or #NULL.
968 *
969 * Gets the list of shared folders and their information.
970 *
971 * Return value: #TRUE if the info could be queried successfully, #FALSE
972 * otherwise. If this function returns #FALSE, an error code will be returned in the
973 * @error argument, and *@ret_info_list will be set to #NULL.
974 **/
975 gboolean
shares_get_share_info_list(GSList ** ret_info_list,GError ** error)976 shares_get_share_info_list (GSList **ret_info_list, GError **error)
977 {
978 g_assert (ret_info_list != NULL);
979 g_assert (error == NULL || *error == NULL);
980
981 if (!refresh_if_needed (error)) {
982 *ret_info_list = NULL;
983 return FALSE;
984 }
985
986 *ret_info_list = NULL;
987 g_hash_table_foreach (path_share_info_hash, copy_to_slist_cb, ret_info_list);
988
989 return TRUE;
990 }
991
992 /**
993 * shares_free_share_info_list:
994 * @list: List of #ShareInfo structures, or %NULL.
995 *
996 * Frees a list of #ShareInfo structures as returned by shares_get_share_info_list().
997 **/
998 void
shares_free_share_info_list(GSList * list)999 shares_free_share_info_list (GSList *list)
1000 {
1001 GSList *l;
1002
1003 for (l = list; l; l = l->next) {
1004 shares_free_share_info (l->data);
1005 }
1006
1007 g_slist_free (list);
1008 }
1009
1010 void
shares_set_debug(gboolean error_on_refresh,gboolean error_on_add,gboolean error_on_modify,gboolean error_on_remove)1011 shares_set_debug (gboolean error_on_refresh,
1012 gboolean error_on_add,
1013 gboolean error_on_modify,
1014 gboolean error_on_remove)
1015 {
1016 throw_error_on_refresh = error_on_refresh;
1017 throw_error_on_add = error_on_add;
1018 throw_error_on_modify = error_on_modify;
1019 throw_error_on_remove = error_on_remove;
1020 }
1021