1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /*
3 * libbalsa vfs glue layer library
4 * Copyright (C) 2008 Albrecht Dre� <albrecht.dress@arcor.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25 #include "libbalsa-vfs.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <glib.h>
33 #include <glib/gi18n.h>
34 #include <glib/gstdio.h>
35 #include <gmime/gmime.h>
36 #include "libbalsa.h"
37 #include "misc.h"
38
39 #include <gio/gio.h>
40 #include "gmime-stream-gio.h"
41
42
43 #define LIBBALSA_VFS_ERROR_QUARK (g_quark_from_static_string("libbalsa-vfs"))
44
45
46 #define LIBBALSA_VFS_MIME_ACTION "mime_action"
47
48 #define GIO_INFO_ATTS \
49 G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
50 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
51 G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
52 "access::*"
53
54
55 struct _LibbalsaVfsPriv {
56 gchar * file_uri;
57 gchar * file_utf8;
58 gchar * folder_uri;
59 gchar * mime_type;
60 gchar * charset;
61 LibBalsaTextAttribute text_attr;
62 GFile * gio_gfile;
63 GFileInfo * info;
64 };
65
66
67 static GObjectClass *libbalsa_vfs_parent_class = NULL;
68
69
70 static void libbalsa_vfs_class_init(LibbalsaVfsClass * klass);
71 static void libbalsa_vfs_init(LibbalsaVfs * self);
72 static void libbalsa_vfs_finalize(LibbalsaVfs * self);
73
74
75 gboolean
libbalsa_vfs_local_only(void)76 libbalsa_vfs_local_only(void)
77 {
78 return FALSE;
79 }
80
81
82 GType
libbalsa_vfs_get_type(void)83 libbalsa_vfs_get_type(void)
84 {
85 static GType libbalsa_vfs_type = 0;
86
87 if (!libbalsa_vfs_type) {
88 static const GTypeInfo libbalsa_vfs_type_info = {
89 sizeof(LibbalsaVfsClass), /* class_size */
90 NULL, /* base_init */
91 NULL, /* base_finalize */
92 (GClassInitFunc) libbalsa_vfs_class_init, /* class_init */
93 NULL, /* class_finalize */
94 NULL, /* class_data */
95 sizeof(LibbalsaVfs), /* instance_size */
96 0, /* n_preallocs */
97 (GInstanceInitFunc) libbalsa_vfs_init, /* instance_init */
98 /* no value_table */
99 };
100
101 libbalsa_vfs_type =
102 g_type_register_static(G_TYPE_OBJECT, "LibbalsaVfs",
103 &libbalsa_vfs_type_info, 0);
104 }
105
106 return libbalsa_vfs_type;
107 }
108
109
110 static void
libbalsa_vfs_class_init(LibbalsaVfsClass * klass)111 libbalsa_vfs_class_init(LibbalsaVfsClass * klass)
112 {
113 GObjectClass *gobject_klass = G_OBJECT_CLASS(klass);
114
115 libbalsa_vfs_parent_class = g_type_class_peek(G_TYPE_OBJECT);
116 gobject_klass->finalize =
117 (GObjectFinalizeFunc) libbalsa_vfs_finalize;
118 }
119
120
121 static void
libbalsa_vfs_init(LibbalsaVfs * self)122 libbalsa_vfs_init(LibbalsaVfs * self)
123 {
124 self->priv = NULL;
125 }
126
127
128 static void
libbalsa_vfs_finalize(LibbalsaVfs * self)129 libbalsa_vfs_finalize(LibbalsaVfs * self)
130 {
131 struct _LibbalsaVfsPriv * priv;
132
133 g_return_if_fail(self != NULL);
134 priv = self->priv;
135
136 if (priv) {
137 g_free(priv->file_uri);
138 g_free(priv->file_utf8);
139 g_free(priv->folder_uri);
140 g_free(priv->mime_type);
141 g_free(priv->charset);
142 if (priv->gio_gfile)
143 g_object_unref(priv->gio_gfile);
144 if (priv->info)
145 g_object_unref(priv->info);
146 g_free(priv);
147 }
148
149 libbalsa_vfs_parent_class->finalize(G_OBJECT(self));
150 }
151
152
153 LibbalsaVfs *
libbalsa_vfs_new(void)154 libbalsa_vfs_new(void)
155 {
156 return LIBBALSA_VFS(g_object_new(LIBBALSA_TYPE_VFS, NULL));
157 }
158
159
160 LibbalsaVfs *
libbalsa_vfs_new_from_uri(const gchar * uri)161 libbalsa_vfs_new_from_uri(const gchar * uri)
162 {
163 LibbalsaVfs * retval;
164
165 g_return_val_if_fail(uri, NULL);
166 if (!(retval = libbalsa_vfs_new()))
167 return NULL;
168
169 if (!(retval->priv = g_new0(struct _LibbalsaVfsPriv, 1))) {
170 g_object_unref(G_OBJECT(retval));
171 return NULL;
172 }
173 retval->priv->text_attr = (LibBalsaTextAttribute) -1;
174
175 retval->priv->file_uri = g_strdup(uri);
176 retval->priv->gio_gfile = g_file_new_for_uri(uri);
177
178 return retval;
179 }
180
181
182 /* create a new LibbalsaVfs object by appending text to the existing object
183 * file (note: text is in utf8, not escaped) */
184 LibbalsaVfs *
libbalsa_vfs_append(const LibbalsaVfs * file,const gchar * text)185 libbalsa_vfs_append(const LibbalsaVfs * file, const gchar * text)
186 {
187 gchar * p;
188 gchar * q;
189 LibbalsaVfs * retval;
190
191 g_return_val_if_fail(file, NULL);
192 g_return_val_if_fail(file->priv, NULL);
193 g_return_val_if_fail(file->priv->file_uri, NULL);
194 g_return_val_if_fail(text, NULL);
195
196 /* fake an absolute file name which we can convert to an uri */
197 p = g_strconcat("/", text, NULL);
198 q = g_filename_to_uri(p, NULL, NULL);
199 g_free(p);
200 if (!q)
201 return NULL;
202
203 /* append to the existing uri and create the new object from it */
204 p = g_strconcat(file->priv->file_uri, q + 8, NULL);
205 g_free(q);
206 retval = libbalsa_vfs_new_from_uri(p);
207 g_free(p);
208 return retval;
209 }
210
211
212 /* create a new LibbalsaVfs object by appending filename to the existing
213 * object dir which describes a folder (note: filename is in utf8, not
214 * escaped) */
215 LibbalsaVfs *
libbalsa_vfs_dir_append(const LibbalsaVfs * dir,const gchar * filename)216 libbalsa_vfs_dir_append(const LibbalsaVfs * dir, const gchar * filename)
217 {
218 gchar * p;
219 gchar * q;
220 LibbalsaVfs * retval;
221
222 g_return_val_if_fail(dir, NULL);
223 g_return_val_if_fail(dir->priv, NULL);
224 g_return_val_if_fail(dir->priv->file_uri, NULL);
225 g_return_val_if_fail(filename, NULL);
226
227 /* fake an absolute file name which we can convert to an uri */
228 p = g_strconcat("/", filename, NULL);
229 q = g_filename_to_uri(p, NULL, NULL);
230 g_free(p);
231 if (!q)
232 return NULL;
233
234 /* append to the existing uri and create the new object from it */
235 p = g_strconcat(dir->priv->file_uri, q + 7, NULL);
236 g_free(q);
237 retval = libbalsa_vfs_new_from_uri(p);
238 g_free(p);
239 return retval;
240 }
241
242
243 /* return the text uri of the passed file, removing the last component */
244 const gchar *
libbalsa_vfs_get_folder(const LibbalsaVfs * file)245 libbalsa_vfs_get_folder(const LibbalsaVfs * file)
246 {
247 struct _LibbalsaVfsPriv * priv;
248
249 g_return_val_if_fail(file, NULL);
250 g_return_val_if_fail(file->priv, NULL);
251 priv = file->priv;
252 g_return_val_if_fail(priv->file_uri, NULL);
253
254 if (!priv->folder_uri) {
255 gchar * p;
256
257 if ((priv->folder_uri = g_strdup(priv->file_uri)) &&
258 (p = g_utf8_strrchr(priv->folder_uri, -1, g_utf8_get_char("/"))))
259 *p = '\0';
260 }
261
262 return priv->folder_uri;
263 }
264
265
266 /* return the text uri of the passed file */
267 const gchar *
libbalsa_vfs_get_uri(const LibbalsaVfs * file)268 libbalsa_vfs_get_uri(const LibbalsaVfs * file)
269 {
270 g_return_val_if_fail(file, NULL);
271 g_return_val_if_fail(file->priv, NULL);
272 g_return_val_if_fail(file->priv->file_uri, NULL);
273
274 return file->priv->file_uri;
275 }
276
277
278 /* return the text uri of the passed file as utf8 string (%xx replaced) */
279 const gchar *
libbalsa_vfs_get_uri_utf8(const LibbalsaVfs * file)280 libbalsa_vfs_get_uri_utf8(const LibbalsaVfs * file)
281 {
282 struct _LibbalsaVfsPriv * priv;
283
284 g_return_val_if_fail(file, NULL);
285 g_return_val_if_fail(file->priv, NULL);
286 priv = file->priv;
287 g_return_val_if_fail(priv->file_uri, NULL);
288
289 if (!priv->file_utf8) {
290 gchar * p;
291 gchar * q;
292
293 if (!(p = priv->file_utf8 = g_malloc(strlen(priv->file_uri) + 1)))
294 return NULL;
295 q = priv->file_uri;
296 while (*q != '\0') {
297 if (*q == '%') {
298 if (g_ascii_isxdigit(q[1]) && g_ascii_isxdigit(q[2])) {
299 gint val = (g_ascii_xdigit_value(q[1]) << 4) +
300 g_ascii_xdigit_value(q[2]);
301 *p++ = (gchar) val;
302 q += 3;
303 } else
304 *p++ = *q++; /* hmmm - shouldn't happen! */
305 } else
306 *p++ = *q++;
307 }
308 *p = '\0';
309 }
310
311 return priv->file_utf8;
312 }
313
314
315 const gchar *
libbalsa_vfs_get_basename_utf8(const LibbalsaVfs * file)316 libbalsa_vfs_get_basename_utf8(const LibbalsaVfs * file)
317 {
318 const gchar * uri_utf8 = libbalsa_vfs_get_uri_utf8(file);
319 const gchar * p;
320
321 if (uri_utf8 &&
322 (p = g_utf8_strrchr(uri_utf8, -1, g_utf8_get_char("/"))))
323 return p + 1;
324 else
325 return NULL;
326 }
327
328
329 const gchar *
libbalsa_vfs_get_mime_type(const LibbalsaVfs * file)330 libbalsa_vfs_get_mime_type(const LibbalsaVfs * file)
331 {
332 struct _LibbalsaVfsPriv * priv;
333
334 g_return_val_if_fail(file, NULL);
335 g_return_val_if_fail(file->priv, NULL);
336 priv = file->priv;
337 g_return_val_if_fail(priv->file_uri, NULL);
338
339 if (!priv->mime_type) {
340 /* use GIO to determine the mime type of the file */
341 g_return_val_if_fail(priv->gio_gfile, FALSE);
342
343 if (!priv->info)
344 priv->info =
345 g_file_query_info(priv->gio_gfile, GIO_INFO_ATTS,
346 G_FILE_QUERY_INFO_NONE, NULL, NULL);
347 if (priv->info)
348 priv->mime_type =
349 g_strdup(g_file_info_get_attribute_string(priv->info,
350 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE));
351
352 /* always fall back to application/octet-stream */
353 if (!priv->mime_type)
354 priv->mime_type = g_strdup("application/octet-stream");
355 }
356
357 return priv->mime_type;
358 }
359
360
361 const gchar *
libbalsa_vfs_get_charset(const LibbalsaVfs * file)362 libbalsa_vfs_get_charset(const LibbalsaVfs * file)
363 {
364 struct _LibbalsaVfsPriv * priv;
365
366 g_return_val_if_fail(file, NULL);
367 g_return_val_if_fail(file->priv, NULL);
368 priv = file->priv;
369 g_return_val_if_fail(priv->file_uri, NULL);
370
371 if (!priv->charset && priv->text_attr == (LibBalsaTextAttribute) -1) {
372 libbalsa_vfs_get_text_attr(file);
373
374 if (!(priv->text_attr & LIBBALSA_TEXT_HI_BIT))
375 priv->charset = g_strdup("us-ascii");
376 else if (priv->text_attr & LIBBALSA_TEXT_HI_UTF8)
377 priv->charset = g_strdup("utf-8");
378 }
379
380 return priv->charset;
381 }
382
383
384 LibBalsaTextAttribute
libbalsa_vfs_get_text_attr(const LibbalsaVfs * file)385 libbalsa_vfs_get_text_attr(const LibbalsaVfs * file)
386 {
387 struct _LibbalsaVfsPriv * priv;
388
389 g_return_val_if_fail(file, 0);
390 g_return_val_if_fail(file->priv, 0);
391 priv = file->priv;
392 g_return_val_if_fail(priv->file_uri, 0);
393
394 if (priv->text_attr == (LibBalsaTextAttribute) -1) {
395 GInputStream * stream;
396
397 /* use GIO to determine the text attributes of the file */
398 g_return_val_if_fail(priv->gio_gfile, 0);
399 priv->text_attr = 0;
400
401 /* read and check - see libbalsa_text_attr_file() */
402 if ((stream = G_INPUT_STREAM(g_file_read(priv->gio_gfile, NULL, NULL))) != NULL) {
403 gchar buf[1024];
404 gchar *new_chars = buf;
405 gboolean has_esc = FALSE;
406 gboolean has_hi_bit = FALSE;
407 gboolean has_hi_ctrl = FALSE;
408 gboolean is_utf8 = TRUE;
409 gssize bytes_read;
410
411 while ((is_utf8 || (!has_esc || !has_hi_bit || !has_hi_ctrl)) &&
412 ((bytes_read = g_input_stream_read(stream,
413 new_chars,
414 (sizeof buf) - (new_chars - buf) - 1,
415 NULL,
416 NULL)) > 0)) {
417 new_chars[bytes_read] = '\0';
418
419 if (!has_esc || !has_hi_bit || !has_hi_ctrl) {
420 guchar * p;
421
422 for (p = (guchar *) new_chars; *p; p++)
423 if (*p == 0x1b)
424 has_esc = TRUE;
425 else if (*p >= 0x80) {
426 has_hi_bit = TRUE;
427 if (*p <= 0x9f)
428 has_hi_ctrl = TRUE;
429 }
430 }
431
432 if (is_utf8) {
433 const gchar *end;
434
435 new_chars = buf;
436 if (!g_utf8_validate(buf, -1, &end)) {
437 if (g_utf8_get_char_validated(end, -1) == (gunichar) (-1))
438 is_utf8 = FALSE;
439 else
440 /* copy any remaining bytes, including the
441 * terminating '\0', to start of buffer */
442 while ((*new_chars = *end++) != '\0')
443 new_chars++;
444 }
445 }
446 }
447
448 g_object_unref(stream);
449
450 if (has_esc)
451 priv->text_attr |= LIBBALSA_TEXT_ESC;
452 if (has_hi_bit)
453 priv->text_attr |= LIBBALSA_TEXT_HI_BIT;
454 if (has_hi_ctrl)
455 priv->text_attr |= LIBBALSA_TEXT_HI_CTRL;
456 if (is_utf8 && has_hi_bit)
457 priv->text_attr |= LIBBALSA_TEXT_HI_UTF8;
458 }
459 }
460
461 return priv->text_attr;
462 }
463
464
465 guint64
libbalsa_vfs_get_size(const LibbalsaVfs * file)466 libbalsa_vfs_get_size(const LibbalsaVfs * file)
467 {
468 struct _LibbalsaVfsPriv * priv;
469
470 g_return_val_if_fail(file, 0);
471 g_return_val_if_fail(file->priv, 0);
472 priv = file->priv;
473 g_return_val_if_fail(priv->file_uri, 0);
474
475 /* use GIO to determine the size of the file */
476 g_return_val_if_fail(priv->gio_gfile, 0);
477
478 if (!priv->info)
479 priv->info =
480 g_file_query_info(priv->gio_gfile, GIO_INFO_ATTS,
481 G_FILE_QUERY_INFO_NONE, NULL, NULL);
482 if (priv->info)
483 return g_file_info_get_attribute_uint64(priv->info,
484 G_FILE_ATTRIBUTE_STANDARD_SIZE);
485
486 return 0;
487 }
488
489
490 /* get a GMime stream for the passed file, either read-only or in
491 * read-write mode */
492 GMimeStream *
libbalsa_vfs_create_stream(const LibbalsaVfs * file,mode_t mode,gboolean rdwr,GError ** err)493 libbalsa_vfs_create_stream(const LibbalsaVfs * file, mode_t mode,
494 gboolean rdwr, GError ** err)
495 {
496 struct _LibbalsaVfsPriv * priv;
497
498 g_return_val_if_fail(file, NULL);
499 g_return_val_if_fail(file->priv, NULL);
500 priv = file->priv;
501 g_return_val_if_fail(priv->file_uri, NULL);
502
503 /* use GIO to create a GMime stream */
504 g_return_val_if_fail(priv->gio_gfile, NULL);
505
506 return g_mime_stream_gio_new(priv->gio_gfile);
507 }
508
509
510 /* return TRUE if the passed file exists */
511 gboolean
libbalsa_vfs_file_exists(const LibbalsaVfs * file)512 libbalsa_vfs_file_exists(const LibbalsaVfs * file)
513 {
514 gboolean result = FALSE;
515 struct _LibbalsaVfsPriv * priv;
516
517 g_return_val_if_fail(file, FALSE);
518 g_return_val_if_fail(file->priv, FALSE);
519 priv = file->priv;
520 g_return_val_if_fail(priv->file_uri, FALSE);
521
522 /* use GIO to get the file's attributes - fails if the file does not exist */
523 g_return_val_if_fail(priv->gio_gfile, 0);
524
525 if (!priv->info)
526 priv->info =
527 g_file_query_info(priv->gio_gfile, GIO_INFO_ATTS,
528 G_FILE_QUERY_INFO_NONE, NULL, NULL);
529 result = priv->info != NULL;
530
531 return result;
532 }
533
534
535 gboolean
libbalsa_vfs_is_regular_file(const LibbalsaVfs * file,GError ** err)536 libbalsa_vfs_is_regular_file(const LibbalsaVfs * file, GError **err)
537 {
538 gboolean result = FALSE;
539 struct _LibbalsaVfsPriv * priv;
540
541 g_return_val_if_fail(file, FALSE);
542 g_return_val_if_fail(file->priv, FALSE);
543 priv = file->priv;
544 g_return_val_if_fail(priv->file_uri, FALSE);
545
546 /* use GIO to check if the file is a regular one which can be read */
547 g_return_val_if_fail(priv->gio_gfile, 0);
548
549 if (!priv->info)
550 priv->info =
551 g_file_query_info(priv->gio_gfile, GIO_INFO_ATTS,
552 G_FILE_QUERY_INFO_NONE, NULL, err);
553 if (priv->info) {
554 if (g_file_info_get_file_type(priv->info) != G_FILE_TYPE_REGULAR)
555 g_set_error(err, LIBBALSA_VFS_ERROR_QUARK, -1,
556 _("not a regular file"));
557 else if (!g_file_info_get_attribute_boolean(priv->info,
558 G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
559 /* the read flag may not be set for some remote file systems (like smb),
560 * so try to actually open the file... */
561 GFileInputStream * stream = g_file_read(priv->gio_gfile, NULL, err);
562 if (stream) {
563 g_object_unref(stream);
564 result = TRUE;
565 }
566 } else
567 result = TRUE;
568 }
569
570 return result;
571 }
572
573
574 /* unlink the passed file, return 0 on success and -1 on error */
575 gint
libbalsa_vfs_file_unlink(const LibbalsaVfs * file,GError ** err)576 libbalsa_vfs_file_unlink(const LibbalsaVfs * file, GError **err)
577 {
578 gint result = -1;
579 struct _LibbalsaVfsPriv * priv;
580
581 g_return_val_if_fail(file, -1);
582 g_return_val_if_fail(file->priv, -1);
583 priv = file->priv;
584 g_return_val_if_fail(priv->file_uri, -1);
585
586 /* use GIO to delete the file */
587 g_return_val_if_fail(priv->gio_gfile, -1);
588 if (g_file_delete(priv->gio_gfile, NULL, err))
589 result = 0;
590
591 return result;
592 }
593
594
595 gboolean
libbalsa_vfs_launch_app(const LibbalsaVfs * file,GObject * object,GError ** err)596 libbalsa_vfs_launch_app(const LibbalsaVfs * file, GObject * object, GError **err)
597 {
598 GAppInfo *app;
599 GList * args;
600 gboolean result;
601
602 g_return_val_if_fail(file != NULL, FALSE);
603 g_return_val_if_fail(object != NULL, FALSE);
604
605 app = G_APP_INFO(g_object_get_data(object, LIBBALSA_VFS_MIME_ACTION));
606 if (!app) {
607 g_set_error(err, LIBBALSA_VFS_ERROR_QUARK, -1,
608 _("Cannot launch, missing application"));
609 return FALSE;
610 }
611 args = g_list_prepend(NULL, file->priv->gio_gfile);
612 result = g_app_info_launch(app, args, NULL, err);
613 g_list_free(args);
614 return result;
615 }
616
617
618 gboolean
libbalsa_vfs_launch_app_for_body(LibBalsaMessageBody * mime_body,GObject * object,GError ** err)619 libbalsa_vfs_launch_app_for_body(LibBalsaMessageBody * mime_body,
620 GObject * object, GError **err)
621 {
622 gchar *uri;
623 LibbalsaVfs * file;
624 gboolean result;
625
626 g_return_val_if_fail(mime_body != NULL, FALSE);
627 g_return_val_if_fail(object != NULL, FALSE);
628
629 if (!libbalsa_message_body_save_temporary(mime_body, err))
630 return FALSE;
631
632 uri = g_filename_to_uri(mime_body->temp_filename, NULL, NULL);
633 file = libbalsa_vfs_new_from_uri(uri);
634 g_free(uri);
635 result = libbalsa_vfs_launch_app(file,object , err);
636 g_object_unref(file);
637
638 return result;
639 }
640
641
642 gchar *
libbalsa_vfs_content_description(const gchar * mime_type)643 libbalsa_vfs_content_description(const gchar * mime_type)
644 {
645 g_return_val_if_fail(mime_type != NULL, NULL);
646
647 return g_content_type_get_description(mime_type);
648 }
649
650 gchar *
libbalsa_vfs_content_type_of_buffer(const guchar * buffer,gsize length)651 libbalsa_vfs_content_type_of_buffer(const guchar * buffer,
652 gsize length)
653 {
654 gchar * retval;
655 gboolean content_uncertain;
656
657 g_return_val_if_fail(buffer != NULL, NULL);
658 g_return_val_if_fail(length > 0, NULL);
659
660 retval = g_content_type_guess(NULL, buffer, length, &content_uncertain);
661 if (content_uncertain) {
662 g_free(retval);
663 retval = g_strdup("application/octet-stream");
664 }
665 return retval;
666 }
667
668
669 static void
gio_add_vfs_menu_item(GtkMenu * menu,GAppInfo * app,GCallback callback,gpointer data)670 gio_add_vfs_menu_item(GtkMenu * menu, GAppInfo *app, GCallback callback,
671 gpointer data)
672 {
673 gchar *menu_label =
674 g_strdup_printf(_("Open with %s"), g_app_info_get_name(app));
675 GtkWidget *menu_item = gtk_menu_item_new_with_label (menu_label);
676
677 g_object_ref(G_OBJECT(app));
678 g_object_set_data_full(G_OBJECT(menu_item), LIBBALSA_VFS_MIME_ACTION,
679 app, g_object_unref);
680 g_signal_connect(G_OBJECT (menu_item), "activate", callback, data);
681 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
682 g_free(menu_label);
683 }
684
685
686 /* fill the passed menu with vfs items */
687 void
libbalsa_vfs_fill_menu_by_content_type(GtkMenu * menu,const gchar * content_type,GCallback callback,gpointer data)688 libbalsa_vfs_fill_menu_by_content_type(GtkMenu * menu,
689 const gchar * content_type,
690 GCallback callback, gpointer data)
691 {
692 GList* list;
693 GAppInfo *def_app;
694 GList *app_list;
695
696 g_return_if_fail(data != NULL);
697
698 if ((def_app = g_app_info_get_default_for_type(content_type, FALSE)))
699 gio_add_vfs_menu_item(menu, def_app, callback, data);
700
701 app_list = g_app_info_get_all_for_type(content_type);
702 for (list = app_list; list; list = g_list_next(list)) {
703 GAppInfo *app = G_APP_INFO(list->data);
704
705 if (app && g_app_info_should_show(app) &&
706 (!def_app || !g_app_info_equal(app, def_app)))
707 gio_add_vfs_menu_item(menu, app, callback, data);
708 }
709 if (def_app)
710 g_object_unref(def_app);
711 if (app_list) {
712 g_list_foreach(app_list, (GFunc) g_object_unref, NULL);
713 g_list_free(app_list);
714 }
715 }
716
717 GtkWidget *
libbalsa_vfs_mime_button(LibBalsaMessageBody * mime_body,const gchar * content_type,GCallback callback,gpointer data)718 libbalsa_vfs_mime_button(LibBalsaMessageBody * mime_body,
719 const gchar * content_type,
720 GCallback callback, gpointer data)
721 {
722 GtkWidget *button = NULL;
723 gchar *msg;
724 GAppInfo *app = g_app_info_get_default_for_type(content_type, FALSE);
725
726 if (app) {
727 msg = g_strdup_printf(_("Open _part with %s"), g_app_info_get_name(app));
728 button = gtk_button_new_with_mnemonic(msg);
729 g_object_set_data_full(G_OBJECT(button), LIBBALSA_VFS_MIME_ACTION,
730 (gpointer) app, g_object_unref);
731 g_free(msg);
732
733 g_signal_connect(G_OBJECT(button), "clicked",
734 callback, data);
735 }
736
737 return button;
738 }
739