From 94c0dae8a956de63965df741aa58765e10b72eee Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Thu, 7 Dec 2017 17:51:44 +0100 Subject: [PATCH] Introduce the MimeHandler class --- include/Desktop/mime.h | 3 +- include/Desktop/mimehandler.h | 70 +++++++++ include/Desktop/project.conf | 5 +- src/mime.c | 4 +- src/mimehandler.c | 257 ++++++++++++++++++++++++++++++++++ src/project.conf | 2 +- 6 files changed, 336 insertions(+), 5 deletions(-) create mode 100644 include/Desktop/mimehandler.h create mode 100644 src/mimehandler.c diff --git a/include/Desktop/mime.h b/include/Desktop/mime.h index 32e9b33..5b1b416 100644 --- a/include/Desktop/mime.h +++ b/include/Desktop/mime.h @@ -32,6 +32,7 @@ # define LIBDESKTOP_DESKTOP_MIME_H # include +# include "mimehandler.h" /* Mime */ @@ -47,7 +48,7 @@ Mime * mime_new(GtkIconTheme * theme); void mime_delete(Mime * mime); /* accessors */ -char const * mime_get_handler(Mime * mime, char const * type, +MimeHandler * mime_get_handler(Mime * mime, char const * type, char const * action); int mime_set_handler(Mime * mime, char const * type, char const * action, char const * handler); diff --git a/include/Desktop/mimehandler.h b/include/Desktop/mimehandler.h new file mode 100644 index 0000000..0b7ba9a --- /dev/null +++ b/include/Desktop/mimehandler.h @@ -0,0 +1,70 @@ +/* $Id$ */ +/* Copyright (c) 2017 Pierre Pronchery */ +/* This file is part of DeforaOS Desktop libDesktop */ +/* All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + + +#ifndef LIBDESKTOP_DESKTOP_MIMEHANDLER_H +# define LIBDESKTOP_DESKTOP_MIMEHANDLER_H + +# include +# include + + +/* MimeHandler */ +/* types */ +typedef struct _Hash MimeHandler; + +typedef enum _MimeHandlerType +{ + MIME_HANDLER_TYPE_UNKNOWN = 0, + MIME_HANDLER_TYPE_APPLICATION, + MIME_HANDLER_TYPE_DIRECTORY, + MIME_HANDLER_TYPE_URL +} MimeHandlerType; + + +/* functions */ +MimeHandler * mimehandler_new(void); +MimeHandler * mimehandler_new_load(String const * name); +void mimehandler_delete(MimeHandler * handler); + +/* accessors */ +int mimehandler_can_display(MimeHandler * handler); +int mimehandler_can_execute(MimeHandler * handler); + +String const * mimehandler_get_name(MimeHandler * handler); +String const * mimehandler_get_program(MimeHandler * handler); +MimeHandlerType mimehandler_get_type(MimeHandler * handler); + +int mimehandler_is_hidden(MimeHandler * handler); + +/* useful */ +int mimehandler_load_by_name(MimeHandler * handler, String const * name); +int mimehandler_save(MimeHandler * handler); + +#endif /* !LIBDESKTOP_DESKTOP_MIMEHANDLER_H */ diff --git a/include/Desktop/project.conf b/include/Desktop/project.conf index 4f50c06..7fd3ac5 100644 --- a/include/Desktop/project.conf +++ b/include/Desktop/project.conf @@ -1,4 +1,4 @@ -includes=about.h,accel.h,compat.h,help.h,menubar.h,message.h,mime.h,toolbar.h,widget.h +includes=about.h,accel.h,compat.h,help.h,menubar.h,message.h,mime.h,mimehandler.h,toolbar.h,widget.h dist=Makefile [about.h] @@ -22,6 +22,9 @@ install=$(INCLUDEDIR)/Desktop [mime.h] install=$(INCLUDEDIR)/Desktop +[mimehandler.h] +install=$(INCLUDEDIR)/Desktop + [toolbar.h] install=$(INCLUDEDIR)/Desktop diff --git a/src/mime.c b/src/mime.c index 1fea7a7..dd337a9 100644 --- a/src/mime.c +++ b/src/mime.c @@ -225,7 +225,7 @@ void mime_delete(Mime * mime) /* accessors */ /* mime_get_handler */ -char const * mime_get_handler(Mime * mime, char const * type, +MimeHandler * mime_get_handler(Mime * mime, char const * type, char const * action) { char const * program; @@ -250,7 +250,7 @@ char const * mime_get_handler(Mime * mime, char const * type, q[2] = '\0'; program = config_get(mime->config, p, action); free(p); - return program; + return mimehandler_new_load(program); } diff --git a/src/mimehandler.c b/src/mimehandler.c new file mode 100644 index 0000000..f13fd4b --- /dev/null +++ b/src/mimehandler.c @@ -0,0 +1,257 @@ +/* $Id$ */ +/* Copyright (c) 2017 Pierre Pronchery */ +/* This file is part of DeforaOS libDesktop */ +/* All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + + +#include +#include +#include +#include "Desktop.h" + + +/* private */ +/* constants */ +#define EXTENSION ".desktop" +#define SECTION "Desktop Entry" + + +/* public */ +/* functions */ +/* mimehandler_new */ +MimeHandler * mimehandler_new(void) +{ + return config_new(); +} + + +/* mimehandler_new_load */ +MimeHandler * mimehandler_new_load(String const * name) +{ + MimeHandler * handler; + + if((handler = mimehandler_new()) == NULL) + return NULL; + if(mimehandler_load_by_name(handler, name) != 0) + { + mimehandler_delete(handler); + return NULL; + } + return handler; +} + + +/* mimehandler_delete */ +void mimehandler_delete(MimeHandler * handler) +{ + config_delete(handler); +} + + +/* accessors */ +/* mimehandler_can_display */ +int mimehandler_can_display(MimeHandler * handler) +{ + String const * p; + + if((p = config_get(handler, SECTION, "NoDisplay")) == NULL) + return 1; + return (string_compare(p, "true") == 0) ? 1 : 0; +} + + +/* mimehandler_can_execute */ +static int _can_execute_access(String const * path, int mode); +static int _can_execute_access_path(String const * path, + String const * filename, int mode); + +int mimehandler_can_execute(MimeHandler * handler) +{ + String const * p; + + if(mimehandler_get_type(handler) != MIME_HANDLER_TYPE_APPLICATION) + return 0; + if((p = config_get(handler, SECTION, "TryExec")) == NULL) + return 1; + return _can_execute_access(p, X_OK); +} + +static int _can_execute_access(String const * path, int mode) +{ + int ret = -1; + String const * p; + String * q; + String * last; + + if(path[0] == '/') + return access(path, mode); + if((p = getenv("PATH")) == NULL) + return 0; + if((q = string_new(p)) == NULL) + return 0; + for(p = strtok_r(q, ":", &last); p != NULL; + p = strtok_r(NULL, ":", &last)) + ret = _can_execute_access_path(p, path, mode); + string_delete(q); + return ret; +} + +static int _can_execute_access_path(String const * path, + String const * filename, int mode) +{ + int ret; + String * p; + + if((p = string_new_append(path, "/", filename, NULL)) == NULL) + return -1; + ret = access(p, mode); + string_delete(p); + return ret; +} + + +/* mimehandler_get_name */ +String const * mimehandler_get_name(MimeHandler * handler) +{ + return config_get(handler, SECTION, "Name"); +} + + +/* mimehandler_get_program */ +String const * mimehandler_get_program(MimeHandler * handler) +{ + switch(mimehandler_get_type(handler)) + { + case MIME_HANDLER_TYPE_APPLICATION: + /* XXX may be a format string */ + return config_get(handler, SECTION, "Exec"); + case MIME_HANDLER_TYPE_DIRECTORY: + case MIME_HANDLER_TYPE_UNKNOWN: + case MIME_HANDLER_TYPE_URL: + return NULL; + } + return NULL; +} + + +/* mimehandler_get_type */ +MimeHandlerType mimehandler_get_type(MimeHandler * handler) +{ + String const * type; + struct + { + String const * name; + MimeHandlerType type; + } types[] = + { + { "Application",MIME_HANDLER_TYPE_APPLICATION }, + { "Directory", MIME_HANDLER_TYPE_DIRECTORY }, + { "URL", MIME_HANDLER_TYPE_URL } + }; + size_t i; + + if((type = config_get(handler, SECTION, "Type")) == NULL) + return MIME_HANDLER_TYPE_UNKNOWN; + for(i = 0; i < sizeof(types) / sizeof(*types); i++) + if(string_compare(types[i].name, type) == 0) + return types[i].type; + return MIME_HANDLER_TYPE_UNKNOWN; +} + + +/* mimehandler_is_hidden */ +int mimehandler_is_hidden(MimeHandler * handler) +{ + String const * p; + + if((p = config_get(handler, SECTION, "Hidden")) == NULL) + return 0; + return (string_compare(p, "true") == 0) ? 1 : 0; +} + + +/* useful */ +/* mimehandler_load_by_name */ +static int _load_by_name_path(MimeHandler * handler, String const * name, + String const * path); + +int mimehandler_load_by_name(MimeHandler * handler, String const * name) +{ + int ret; + String const fallback[] = ".local/share"; + String const * path; + String const * homedir; + String * p; + String const * q; + String * last; + + /* use $XDG_DATA_HOME if set and not empty */ + if((path = getenv("XDG_DATA_HOME")) != NULL && strlen(path) > 0 + && _load_by_name_path(handler, name, path) == 0) + return 0; + /* fallback to "$HOME/.local/share" */ + if((homedir = getenv("HOME")) == NULL) + homedir = g_get_home_dir(); + if((p = string_new_append(homedir, "/", fallback, NULL)) == NULL) + return -1; + ret = _load_by_name_path(handler, name, p); + string_delete(p); + if(ret == 0) + return ret; + /* read through every XDG application folder */ + if((path = getenv("XDG_DATA_DIRS")) == NULL || strlen(path) == 0) + path = "/usr/local/share:/usr/share"; + if((p = string_new(path)) == NULL) + return -1; + for(q = strtok_r(p, ":", &last); q != NULL; + q = strtok_r(NULL, ":", &last)) + if((ret = _load_by_name_path(handler, name, q)) == 0) + break; + string_delete(p); + return ret; +} + +static int _load_by_name_path(MimeHandler * handler, String const * name, + String const * path) +{ + int ret; + String const applications[] = "/applications/"; + String * pathname; + + if((pathname = string_new_append(path, applications, name, EXTENSION, + NULL)) == NULL) + return -1; + ret = (config_reset(handler) == 0 + && config_load(handler, pathname) == 0 + && mimehandler_get_type(handler) + != MIME_HANDLER_TYPE_UNKNOWN + && mimehandler_get_name(handler) != NULL + && mimehandler_is_hidden(handler) == 0) + ? 0 : -1; + string_delete(pathname); + return ret; +} diff --git a/src/project.conf b/src/project.conf index 51d799b..4c28101 100644 --- a/src/project.conf +++ b/src/project.conf @@ -12,7 +12,7 @@ dist=Makefile [libDesktop] type=library -sources=about.c,accel.c,compat.c,help.c,menubar.c,message.c,mime.c,toolbar.c,widget.c +sources=about.c,accel.c,compat.c,help.c,menubar.c,message.c,mime.c,mimehandler.c,toolbar.c,widget.c install=$(PREFIX)/lib [help.c]