1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 caja-link.c: .desktop link files.
4
5 Copyright (C) 2001 Red Hat, Inc.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the historicalied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the
19 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21
22 Authors: Jonathan Blandford <jrb@redhat.com>
23 Alexander Larsson <alexl@redhat.com>
24 */
25
26 #include <config.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32
33 #include <eel/eel-vfs-extensions.h>
34
35 #include "caja-link.h"
36 #include "caja-directory-notify.h"
37 #include "caja-directory.h"
38 #include "caja-file-utilities.h"
39 #include "caja-file.h"
40 #include "caja-program-choosing.h"
41 #include "caja-icon-names.h"
42
43 #define MAIN_GROUP "Desktop Entry"
44
45 #define CAJA_LINK_GENERIC_TAG "Link"
46 #define CAJA_LINK_TRASH_TAG "X-caja-trash"
47 #define CAJA_LINK_MOUNT_TAG "FSDevice"
48 #define CAJA_LINK_HOME_TAG "X-caja-home"
49
50 static gboolean
is_link_mime_type(const char * mime_type)51 is_link_mime_type (const char *mime_type)
52 {
53 if (mime_type != NULL &&
54 (g_ascii_strcasecmp (mime_type, "application/x-mate-app-info") == 0 ||
55 g_ascii_strcasecmp (mime_type, "application/x-desktop") == 0))
56 {
57 return TRUE;
58 }
59
60 return FALSE;
61 }
62
63 static gboolean
is_local_file_a_link(const char * uri)64 is_local_file_a_link (const char *uri)
65 {
66 gboolean link;
67 GFile *file;
68 GFileInfo *info;
69 GError *error;
70
71 error = NULL;
72 link = FALSE;
73
74 file = g_file_new_for_uri (uri);
75
76 info = g_file_query_info (file,
77 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
78 0, NULL, &error);
79 if (info)
80 {
81 link = is_link_mime_type (g_file_info_get_content_type (info));
82 g_object_unref (info);
83 }
84 else
85 {
86 g_warning ("Error getting info: %s\n", error->message);
87 g_error_free (error);
88 }
89
90 g_object_unref (file);
91
92 return link;
93 }
94
95 static gboolean
_g_key_file_load_from_gfile(GKeyFile * key_file,GFile * file,GKeyFileFlags flags,GError ** error)96 _g_key_file_load_from_gfile (GKeyFile *key_file,
97 GFile *file,
98 GKeyFileFlags flags,
99 GError **error)
100 {
101 char *data;
102 gsize len;
103 gboolean res;
104
105 if (!g_file_load_contents (file, NULL, &data, &len, NULL, error))
106 {
107 return FALSE;
108 }
109
110 res = g_key_file_load_from_data (key_file, data, len, flags, error);
111
112 g_free (data);
113
114 return res;
115 }
116
117 static gboolean
_g_key_file_save_to_gfile(GKeyFile * key_file,GFile * file,GError ** error)118 _g_key_file_save_to_gfile (GKeyFile *key_file,
119 GFile *file,
120 GError **error)
121 {
122 char *data;
123 gsize len;
124
125 data = g_key_file_to_data (key_file, &len, error);
126 if (data == NULL)
127 {
128 return FALSE;
129 }
130
131 if (!g_file_replace_contents (file,
132 data, len,
133 NULL, FALSE,
134 G_FILE_CREATE_NONE,
135 NULL, NULL, error))
136 {
137 g_free (data);
138 return FALSE;
139 }
140 g_free (data);
141 return TRUE;
142 }
143
144
145
146 static GKeyFile *
_g_key_file_new_from_uri(const char * uri,GKeyFileFlags flags,GError ** error)147 _g_key_file_new_from_uri (const char *uri,
148 GKeyFileFlags flags,
149 GError **error)
150 {
151 GKeyFile *key_file;
152 GFile *file;
153
154 file = g_file_new_for_uri (uri);
155 key_file = g_key_file_new ();
156 if (!_g_key_file_load_from_gfile (key_file, file, flags, error))
157 {
158 g_key_file_free (key_file);
159 key_file = NULL;
160 }
161 g_object_unref (file);
162 return key_file;
163 }
164
165 static char *
slurp_key_string(const char * uri,const char * keyname,gboolean localize)166 slurp_key_string (const char *uri,
167 const char *keyname,
168 gboolean localize)
169 {
170 GKeyFile *key_file;
171 char *result;
172
173 key_file = _g_key_file_new_from_uri (uri, G_KEY_FILE_NONE, NULL);
174 if (key_file == NULL)
175 {
176 return NULL;
177 }
178
179 if (localize)
180 {
181 result = g_key_file_get_locale_string (key_file, MAIN_GROUP, keyname, NULL, NULL);
182 }
183 else
184 {
185 result = g_key_file_get_string (key_file, MAIN_GROUP, keyname, NULL);
186 }
187 g_key_file_free (key_file);
188
189 return result;
190 }
191
192 gboolean
caja_link_local_create(const char * directory_uri,const char * base_name,const char * display_name,const char * image,const char * target_uri,const GdkPoint * point,int screen,gboolean unique_filename,GError ** error)193 caja_link_local_create (const char *directory_uri,
194 const char *base_name,
195 const char *display_name,
196 const char *image,
197 const char *target_uri,
198 const GdkPoint *point,
199 int screen,
200 gboolean unique_filename,
201 GError **error)
202 {
203 char *real_directory_uri;
204 char *contents;
205 GFile *file;
206 GList dummy_list;
207 CajaFileChangesQueuePosition item;
208
209 g_return_val_if_fail (directory_uri != NULL, FALSE);
210 g_return_val_if_fail (base_name != NULL, FALSE);
211 g_return_val_if_fail (display_name != NULL, FALSE);
212 g_return_val_if_fail (target_uri != NULL, FALSE);
213
214 if (eel_uri_is_trash (directory_uri) ||
215 eel_uri_is_search (directory_uri))
216 {
217 return FALSE;
218 }
219
220 if (eel_uri_is_desktop (directory_uri))
221 {
222 real_directory_uri = caja_get_desktop_directory_uri ();
223 }
224 else
225 {
226 real_directory_uri = g_strdup (directory_uri);
227 }
228
229 if (unique_filename)
230 {
231 char *uri;
232
233 uri = caja_ensure_unique_file_name (real_directory_uri,
234 base_name, ".desktop");
235 if (uri == NULL)
236 {
237 g_free (real_directory_uri);
238 return FALSE;
239 }
240 file = g_file_new_for_uri (uri);
241 g_free (uri);
242 }
243 else
244 {
245 char *link_name;
246 GFile *dir;
247
248 link_name = g_strdup_printf ("%s.desktop", base_name);
249
250 /* replace '/' with '-', just in case */
251 g_strdelimit (link_name, "/", '-');
252
253 dir = g_file_new_for_uri (directory_uri);
254 file = g_file_get_child (dir, link_name);
255
256 g_free (link_name);
257 g_object_unref (dir);
258 }
259
260 g_free (real_directory_uri);
261
262 contents = g_strdup_printf ("[Desktop Entry]\n"
263 "Encoding=UTF-8\n"
264 "Name=%s\n"
265 "Type=Link\n"
266 "URL=%s\n"
267 "%s%s\n",
268 display_name,
269 target_uri,
270 image != NULL ? "Icon=" : "",
271 image != NULL ? image : "");
272
273
274 if (!g_file_replace_contents (file,
275 contents, strlen (contents),
276 NULL, FALSE,
277 G_FILE_CREATE_NONE,
278 NULL, NULL, error))
279 {
280 g_free (contents);
281 g_object_unref (file);
282 return FALSE;
283 }
284 g_free (contents);
285
286 dummy_list.data = file;
287 dummy_list.next = NULL;
288 dummy_list.prev = NULL;
289 caja_directory_notify_files_added (&dummy_list);
290
291 if (point != NULL)
292 {
293 item.location = file;
294 item.set = TRUE;
295 item.point.x = point->x;
296 item.point.y = point->y;
297 item.screen = screen;
298 dummy_list.data = &item;
299 dummy_list.next = NULL;
300 dummy_list.prev = NULL;
301
302 caja_directory_schedule_position_set (&dummy_list);
303 }
304
305 g_object_unref (file);
306 return TRUE;
307 }
308
309 static const char *
get_language(void)310 get_language (void)
311 {
312 const char * const *langs_pointer;
313 int i;
314
315 langs_pointer = g_get_language_names ();
316 for (i = 0; langs_pointer[i] != NULL; i++)
317 {
318 /* find first without encoding */
319 if (strchr (langs_pointer[i], '.') == NULL)
320 {
321 return langs_pointer[i];
322 }
323 }
324 return NULL;
325 }
326
327 static gboolean
caja_link_local_set_key(const char * uri,const char * key,const char * value,gboolean localize)328 caja_link_local_set_key (const char *uri,
329 const char *key,
330 const char *value,
331 gboolean localize)
332 {
333 gboolean success;
334 GKeyFile *key_file;
335 GFile *file;
336
337 file = g_file_new_for_uri (uri);
338 key_file = g_key_file_new ();
339 if (!_g_key_file_load_from_gfile (key_file, file, G_KEY_FILE_KEEP_COMMENTS, NULL))
340 {
341 g_key_file_free (key_file);
342 g_object_unref (file);
343 return FALSE;
344 }
345 if (localize)
346 {
347 g_key_file_set_locale_string (key_file,
348 MAIN_GROUP,
349 key,
350 get_language (),
351 value);
352 }
353 else
354 {
355 g_key_file_set_string (key_file, MAIN_GROUP, key, value);
356 }
357
358
359 success = _g_key_file_save_to_gfile (key_file, file, NULL);
360 g_key_file_free (key_file);
361 g_object_unref (file);
362 return success;
363 }
364
365 gboolean
caja_link_local_set_text(const char * uri,const char * text)366 caja_link_local_set_text (const char *uri,
367 const char *text)
368 {
369 return caja_link_local_set_key (uri, "Name", text, TRUE);
370 }
371
372
373 gboolean
caja_link_local_set_icon(const char * uri,const char * icon)374 caja_link_local_set_icon (const char *uri,
375 const char *icon)
376 {
377 return caja_link_local_set_key (uri, "Icon", icon, FALSE);
378 }
379
380 char *
caja_link_local_get_text(const char * path)381 caja_link_local_get_text (const char *path)
382 {
383 return slurp_key_string (path, "Name", TRUE);
384 }
385
386 char *
caja_link_local_get_additional_text(const char * path)387 caja_link_local_get_additional_text (const char *path)
388 {
389 /* The comment field of current .desktop files is often bad.
390 * It just contains a copy of the name. This is probably because the
391 * panel shows the comment field as a tooltip.
392 */
393 return NULL;
394 #ifdef THIS_IS_NOT_USED_RIGHT_NOW
395 char *type;
396 char *retval;
397
398 if (!is_local_file_a_link (uri))
399 {
400 return NULL;
401 }
402
403 type = slurp_key_string (path, "Type", FALSE);
404 retval = NULL;
405 if (type == NULL)
406 {
407 return NULL;
408 }
409
410 if (strcmp (type, "Application") == 0)
411 {
412 retval = slurp_key_string (path, "Comment", TRUE);
413 }
414
415 g_free (type);
416
417 return retval;
418 #endif
419 }
420
421 static char *
caja_link_get_link_uri_from_desktop(GKeyFile * key_file,const char * desktop_file_uri)422 caja_link_get_link_uri_from_desktop (GKeyFile *key_file, const char *desktop_file_uri)
423 {
424 char *type = g_key_file_get_string (key_file, MAIN_GROUP, "Type", NULL);
425 if (type == NULL)
426 {
427 return NULL;
428 }
429
430 char *retval = NULL;
431
432 if (strcmp (type, "URL") == 0)
433 {
434 /* Some old broken desktop files use this nonstandard feature, we need handle it though */
435 retval = g_key_file_get_string (key_file, MAIN_GROUP, "Exec", NULL);
436 }
437 else if ((strcmp (type, CAJA_LINK_GENERIC_TAG) == 0) ||
438 (strcmp (type, CAJA_LINK_MOUNT_TAG) == 0) ||
439 (strcmp (type, CAJA_LINK_TRASH_TAG) == 0) ||
440 (strcmp (type, CAJA_LINK_HOME_TAG) == 0))
441 {
442 retval = g_key_file_get_string (key_file, MAIN_GROUP, "URL", NULL);
443 }
444 g_free (type);
445
446 if (retval != NULL && desktop_file_uri != NULL)
447 {
448 /* Handle local file names.
449 * Ideally, we'd be able to use
450 * g_file_parse_name(), but it does not know how to resolve
451 * relative file names, since the base directory is unknown.
452 */
453 char *scheme = g_uri_parse_scheme (retval);
454 if (scheme == NULL)
455 {
456 GFile *file = g_file_new_for_uri (desktop_file_uri);
457 GFile *parent = g_file_get_parent (file);
458 g_object_unref (file);
459
460 if (parent != NULL)
461 {
462 file = g_file_resolve_relative_path (parent, retval);
463 g_free (retval);
464 retval = g_file_get_uri (file);
465 g_object_unref (file);
466 g_object_unref (parent);
467 }
468 }
469 else
470 {
471 g_free (scheme);
472 }
473 }
474
475 return retval;
476 }
477
478 static char *
caja_link_get_link_name_from_desktop(GKeyFile * key_file)479 caja_link_get_link_name_from_desktop (GKeyFile *key_file)
480 {
481 return g_key_file_get_locale_string (key_file, MAIN_GROUP, "Name", NULL, NULL);
482 }
483
484 static char *
caja_link_get_link_icon_from_desktop(GKeyFile * key_file)485 caja_link_get_link_icon_from_desktop (GKeyFile *key_file)
486 {
487 char *icon_uri, *icon, *type;
488
489 icon_uri = g_key_file_get_string (key_file, MAIN_GROUP, "X-Caja-Icon", NULL);
490 if (icon_uri != NULL)
491 {
492 return icon_uri;
493 }
494
495 icon = g_key_file_get_string (key_file, MAIN_GROUP, "Icon", NULL);
496 if (icon != NULL)
497 {
498 if (!g_path_is_absolute (icon))
499 {
500 char *p;
501
502 /* Strip out any extension on non-filename icons. Old desktop files may have this */
503 p = strchr (icon, '.');
504 /* Only strip known icon extensions */
505 if ((p != NULL) &&
506 ((g_ascii_strcasecmp (p, ".png") == 0)
507 || (g_ascii_strcasecmp (p, ".svn") == 0)
508 || (g_ascii_strcasecmp (p, ".jpg") == 0)
509 || (g_ascii_strcasecmp (p, ".xpm") == 0)
510 || (g_ascii_strcasecmp (p, ".bmp") == 0)
511 || (g_ascii_strcasecmp (p, ".jpeg") == 0)))
512 {
513 *p = 0;
514 }
515 }
516 return icon;
517 }
518
519 type = g_key_file_get_string (key_file, MAIN_GROUP, "Type", NULL);
520 if (g_strcmp0 (type, "Application") == 0)
521 {
522 icon = g_strdup ("mate-fs-executable");
523 }
524 else if (g_strcmp0 (type, "Link") == 0)
525 {
526 icon = g_strdup ("mate-dev-symlink");
527 }
528 else if (g_strcmp0 (type, "FSDevice") == 0)
529 {
530 icon = g_strdup ("mate-dev-harddisk");
531 }
532 else if (g_strcmp0 (type, "Directory") == 0)
533 {
534 icon = g_strdup (CAJA_ICON_FOLDER);
535 }
536 else if (g_strcmp0 (type, "Service") == 0 ||
537 g_strcmp0 (type, "ServiceType") == 0)
538 {
539 icon = g_strdup ("mate-fs-web");
540 }
541 else
542 {
543 icon = g_strdup ("mate-fs-regular");
544 }
545 g_free (type);
546
547 return icon;
548 }
549
550 char *
caja_link_local_get_link_uri(const char * uri)551 caja_link_local_get_link_uri (const char *uri)
552 {
553 GKeyFile *key_file;
554 char *retval;
555
556 if (!is_local_file_a_link (uri))
557 {
558 return NULL;
559 }
560
561 key_file = _g_key_file_new_from_uri (uri, G_KEY_FILE_NONE, NULL);
562 if (key_file == NULL)
563 {
564 return NULL;
565 }
566
567 retval = caja_link_get_link_uri_from_desktop (key_file, uri);
568 g_key_file_free (key_file);
569
570 return retval;
571 }
572
573 static gboolean
string_array_contains(char ** array,const char * str)574 string_array_contains (char **array,
575 const char *str)
576 {
577 char **p;
578
579 if (!array)
580 return FALSE;
581
582 for (p = array; *p; p++)
583 if (g_ascii_strcasecmp (*p, str) == 0)
584 {
585 return TRUE;
586 }
587
588 return FALSE;
589 }
590
591 void
caja_link_get_link_info_given_file_contents(const char * file_contents,int link_file_size,const char * file_uri,char ** uri,char ** name,char ** icon,gboolean * is_launcher,gboolean * is_foreign)592 caja_link_get_link_info_given_file_contents (const char *file_contents,
593 int link_file_size,
594 const char *file_uri,
595 char **uri,
596 char **name,
597 char **icon,
598 gboolean *is_launcher,
599 gboolean *is_foreign)
600 {
601 GKeyFile *key_file;
602 char *type;
603 char **only_show_in;
604 char **not_show_in;
605
606 key_file = g_key_file_new ();
607 if (!g_key_file_load_from_data (key_file,
608 file_contents,
609 link_file_size,
610 G_KEY_FILE_NONE,
611 NULL))
612 {
613 g_key_file_free (key_file);
614 return;
615 }
616
617 *uri = caja_link_get_link_uri_from_desktop (key_file, file_uri);
618 *name = caja_link_get_link_name_from_desktop (key_file);
619 *icon = caja_link_get_link_icon_from_desktop (key_file);
620
621 *is_launcher = FALSE;
622 type = g_key_file_get_string (key_file, MAIN_GROUP, "Type", NULL);
623 if (g_strcmp0 (type, "Application") == 0 &&
624 g_key_file_has_key (key_file, MAIN_GROUP, "Exec", NULL))
625 {
626 *is_launcher = TRUE;
627 }
628 g_free (type);
629
630 *is_foreign = FALSE;
631 only_show_in = g_key_file_get_string_list (key_file, MAIN_GROUP,
632 "OnlyShowIn", NULL, NULL);
633 if (only_show_in && !string_array_contains (only_show_in, "MATE"))
634 {
635 *is_foreign = TRUE;
636 }
637 g_strfreev (only_show_in);
638
639 not_show_in = g_key_file_get_string_list (key_file, MAIN_GROUP,
640 "NotShowIn", NULL, NULL);
641 if (not_show_in && string_array_contains (not_show_in, "MATE"))
642 {
643 *is_foreign = TRUE;
644 }
645 g_strfreev (not_show_in);
646
647 g_key_file_free (key_file);
648 }
649