summaryrefslogtreecommitdiffstats
path: root/src/extension/loader.cpp
blob: d409d09baf0572273ad29b2296fb510d992341d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Loader for external plug-ins.
 *
 * Authors:
 *   Moritz Eberl <moritz@semiodesk.com>
 *
 * Copyright (C) 2016 Authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include "loader.h"

#include <gmodule.h>

#include "system.h"
#include <cstring>
#include "dependency.h"
#include "inkscape-version.h"

namespace Inkscape {
namespace Extension {

typedef Implementation::Implementation *(*_getImplementation)();
typedef const gchar *(*_getInkscapeVersion)();

bool Loader::load_dependency(Dependency *dep)
{
    GModule *module = nullptr;
    module = g_module_open(dep->get_name(), (GModuleFlags)0);
    if (module == nullptr) {
        return false;
    }
    return true;
}

/**
 * @brief Load the actual implementation of a plugin supplied by the plugin.
 * @param doc The xml representation of the INX extension configuration.
 * @return The implementation of the extension loaded from the plugin.
 */
Implementation::Implementation *Loader::load_implementation(Inkscape::XML::Document *doc)
{
    try {
        
        Inkscape::XML::Node *repr = doc->root();
        Inkscape::XML::Node *child_repr = repr->firstChild();
        
        // Iterate over the xml content
        while (child_repr != nullptr) {
            char const *chname = child_repr->name();
            if (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
                chname += strlen(INKSCAPE_EXTENSION_NS);
            }
            
            // Deal with dependencies if we have them
            if (!strcmp(chname, "dependency")) {
                Dependency dep = Dependency(child_repr, nullptr); // TODO: Why is "this" not an extension?
                // try to load it
                bool success = load_dependency(&dep);
                if( !success ){
                    // Could not load dependency, we abort
                    const char *res = g_module_error();
                    g_warning("Unable to load dependency %s of plugin %s.\nDetails: %s\n", dep.get_name(), "<todo>", res);
                    return nullptr;
                }
            } 

            // Found a plugin to load
            if (!strcmp(chname, "plugin")) {
                
                // The name of the plugin is actually the library file we want to load
                if (const gchar *name = child_repr->attribute("name")) {
                    GModule *module = nullptr;
                    _getImplementation GetImplementation = nullptr;
                    _getInkscapeVersion GetInkscapeVersion = nullptr;
                    
                    // build the path where to look for the plugin
                    gchar *path = g_build_filename(_baseDirectory.c_str(), name, (char *) nullptr);
                    module = g_module_open(path, G_MODULE_BIND_LOCAL);
                    g_free(path);
                    
                    if (module == nullptr) {
                        // we were not able to load the plugin, write warning and abort
                        const char *res = g_module_error();
                        g_warning("Unable to load extension %s.\nDetails: %s\n", name, res);
                        return nullptr;
                    }

                    // Get a handle to the version function of the module
                    if (g_module_symbol(module, "GetInkscapeVersion", (gpointer *) &GetInkscapeVersion) == FALSE) {
                        // This didn't work, write warning and abort
                        const char *res = g_module_error();
                        g_warning("Unable to load extension %s.\nDetails: %s\n", name, res);
                        return nullptr;
                    }
                    
                    // Get a handle to the function that delivers the implementation
                    if (g_module_symbol(module, "GetImplementation", (gpointer *) &GetImplementation) == FALSE) {
                        // This didn't work, write warning and abort
                        const char *res = g_module_error();
                        g_warning("Unable to load extension %s.\nDetails: %s\n", name, res);
                        return nullptr;
                    }
                    
                    // Get version and test against this version
                    const gchar* version = GetInkscapeVersion();
                    if( strcmp(version, version_string) != 0) {
                        // The versions are different, display warning.
                        g_warning("Plugin was built against Inkscape version %s, this is %s. The plugin might not be compatible.", version, version_string);
                    }
                    
                    
                    Implementation::Implementation *i = GetImplementation();
                    return i;
                }
            } 

            child_repr = child_repr->next();
        }
    } catch (std::exception &e) {
        g_warning("Unable to load extension.");
    }
    return nullptr;
}

} // namespace Extension
} // namespace Inkscape

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace .0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim:filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99: