LCOV - code coverage report
Current view: top level - snapwebsites - plugins.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 283 2.1 %
Date: 2019-12-15 17:13:15 Functions: 2 39 5.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- plugin loader
       2             : // Copyright (c) 2011-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // https://snapwebsites.org/
       5             : // contact@m2osw.com
       6             : //
       7             : // This program is free software; you can redistribute it and/or modify
       8             : // it under the terms of the GNU General Public License as published by
       9             : // the Free Software Foundation; either version 2 of the License, or
      10             : // (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 implied warranty of
      14             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             : // GNU General Public License for more details.
      16             : //
      17             : // You should have received a copy of the GNU General Public License
      18             : // along with this program; if not, write to the Free Software
      19             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      20             : 
      21             : 
      22             : // self
      23             : //
      24             : #include "snapwebsites/plugins.h"
      25             : 
      26             : 
      27             : // snapwebsites lib
      28             : //
      29             : #include "snapwebsites/log.h"
      30             : #include "snapwebsites/snapwebsites.h"
      31             : #include "snapwebsites/qstring_stream.h"
      32             : 
      33             : 
      34             : // snapdev lib
      35             : //
      36             : #include <snapdev/not_used.h>
      37             : 
      38             : 
      39             : // Qt lib
      40             : //
      41             : #include <QDir>
      42             : #include <QMap>
      43             : #include <QFileInfo>
      44             : 
      45             : 
      46             : // C++ lib
      47             : //
      48             : #include <sstream>
      49             : 
      50             : 
      51             : // C lib
      52             : //
      53             : #include <dlfcn.h>
      54             : #include <link.h>
      55             : #include <sys/stat.h>
      56             : 
      57             : 
      58             : // last include
      59             : //
      60             : #include <snapdev/poison.h>
      61             : 
      62             : 
      63             : 
      64             : 
      65             : namespace snap
      66             : {
      67             : namespace plugins
      68             : {
      69             : 
      70           2 : plugin_map_t        g_plugins;
      71           2 : plugin_vector_t     g_ordered_plugins;
      72           2 : QString             g_next_register_name;
      73           2 : QString             g_next_register_filename;
      74           2 : QString             g_next_register_introducer;
      75             : 
      76             : 
      77             : /** \brief Load a complete list of available plugins.
      78             :  *
      79             :  * This is used in the administrator screen to offer users a complete list of
      80             :  * plugins that can be installed.
      81             :  *
      82             :  * \param[in] plugin_paths  The paths to all the Snap plugins.
      83             :  *
      84             :  * \return A list of plugin names.
      85             :  */
      86           0 : snap_string_list list_all(QString const & plugin_paths)
      87             : {
      88             :     // note that we expect the plugin directory to be clean
      89             :     // (we may later check the validity of each directory to make 100% sure
      90             :     // that it includes a corresponding .so file)
      91           0 :     snap_string_list const paths(plugin_paths.split(':'));
      92           0 :     snap_string_list filters;
      93           0 :     filters << "*.so";
      94           0 :     snap_string_list result;
      95           0 :     for(auto const & p : paths)
      96             :     {
      97           0 :         QDir dir(p);
      98             : 
      99           0 :         dir.setSorting(QDir::Name | QDir::IgnoreCase);
     100             : 
     101             :         // TBD: while in development, plugins are in sub-directories
     102             :         //      once installed, they are not...
     103             :         //      maybe we should have some sort of flag to skip on the
     104             :         //      sub-directories once building a package?
     105             :         //
     106           0 :         snap_string_list const sub_dirs(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot));
     107           0 :         for(auto const & s : sub_dirs)
     108             :         {
     109           0 :             QDir sdir(QString("%1/%2").arg(p).arg(s));
     110             : 
     111           0 :             sdir.setSorting(QDir::Name | QDir::IgnoreCase);
     112           0 :             sdir.setNameFilters(filters);
     113             : 
     114           0 :             result << sdir.entryList(QDir::Files);
     115             :         }
     116             : 
     117           0 :         dir.setNameFilters(filters);
     118             : 
     119           0 :         result << dir.entryList(QDir::Files);
     120             :     }
     121             : 
     122             :     // clean up the list
     123           0 :     for(int p(result.size() - 1); p >= 0; --p)
     124             :     {
     125           0 :         if(result[p].length() < 4
     126           0 :         || !result[p].endsWith(".so"))
     127             :         {
     128             :             // this should never happen
     129           0 :             result.removeAt(p);
     130             :         }
     131           0 :         else if(result[p].length() > 6
     132           0 :              && result[p].startsWith("lib"))
     133             :         {
     134             :             // remove the "lib" and ".so"
     135           0 :             result[p] = result[p].mid(3, result[p].length() - 6);
     136             :         }
     137             :         else
     138             :         {
     139             :             // remove the ".so"
     140           0 :             result[p] = result[p].left(result[p].length() - 3);
     141             :         }
     142             :     }
     143             : 
     144           0 :     result << "server";
     145             : 
     146           0 :     result.sort();
     147             : 
     148           0 :     return result;
     149             : }
     150             : 
     151             : 
     152             : /** \brief Load all the plugins.
     153             :  *
     154             :  * Someone who wants to remove a plugin simply deletes it or its
     155             :  * softlink at least.
     156             :  *
     157             :  * \warning
     158             :  * This function CANNOT use glob() to read all the plugins in a directory.
     159             :  * At this point we assume that each website will use more or less of
     160             :  * the installed plugins and thus loading them all is not the right way of
     161             :  * handling the loading. Thus we now get a \p list_of_plugins parameter
     162             :  * with the name of the plugins we want to dlopen().
     163             :  *
     164             :  * \todo
     165             :  * Look into the shared pointers and unloading plugins, if that ever
     166             :  * happens (I don't think it does.)
     167             :  *
     168             :  * \param[in] plugin_paths  The colon (:) separated list of paths to
     169             :  *                          directories with plugins.
     170             :  * \param[in] snap  A pointer to the child object loading these plugins.
     171             :  *                  This pointer gets passed to the bootstrap() signal.
     172             :  * \param[in] server  A pointer to the server to register it as a plugin.
     173             :  * \param[in] list_of_plugins  The list of plugins to load.
     174             :  *
     175             :  * \return true if all the modules were loaded.
     176             :  */
     177           0 : bool load(QString const & plugin_paths, snap_child * snap, plugin_ptr_t server, snap_string_list const & list_of_plugins, QString const & introducer)
     178             : {
     179           0 :     g_plugins.insert("server", server.get());
     180             : 
     181           0 :     snap_string_list const paths(plugin_paths.split(':'));
     182             : 
     183           0 :     bool good(true);
     184           0 :     for(snap_string_list::const_iterator it(list_of_plugins.begin());
     185           0 :                                     it != list_of_plugins.end();
     186             :                                     ++it)
     187             :     {
     188           0 :         QString const name(*it);
     189             : 
     190             :         // the Snap server is already added to the list under that name!
     191             :         //
     192           0 :         if(name == "server")
     193             :         {
     194           0 :             SNAP_LOG_ERROR("error: a plugin cannot be called \"server\".");
     195           0 :             good = false;
     196           0 :             continue;
     197             :         }
     198             : 
     199             :         // in case we get multiple calls to this function we must make sure that
     200             :         // all plugins have a distinct name (i.e. a plugin factory could call
     201             :         // this function to load sub-plugins!)
     202             :         //
     203           0 :         if(exists(name))
     204             :         {
     205           0 :             SNAP_LOG_ERROR("error: two plugins cannot be named the same, found \"")(name)("\" twice.");
     206           0 :             good = false;
     207           0 :             continue;
     208             :         }
     209             : 
     210             :         // make sure the name is one we consider valid; we may end up
     211             :         // using plugin names in scripts and thus want to only support
     212             :         // a small set of characters; any other name is refused by
     213             :         // the verify_plugin_name() function (which prints an error
     214             :         // message already so no need to another one here)
     215             :         //
     216           0 :         if(!verify_plugin_name(name))
     217             :         {
     218           0 :             good = false;
     219           0 :             continue;
     220             :         }
     221             : 
     222             :         // check that the file exists, if not we generate an error
     223             :         //
     224           0 :         QString const filename(find_plugin_filename(paths, name));
     225           0 :         if(filename.isEmpty())
     226             :         {
     227           0 :             SNAP_LOG_ERROR("plugin named \"")(name)("\" not found in the plugin directory. (paths: ")(plugin_paths)(")");
     228           0 :             good = false;
     229           0 :             continue;
     230             :         }
     231             : 
     232             :         // TBD: Use RTLD_NOW instead of RTLD_LAZY in DEBUG mode
     233             :         //      so we discover missing symbols would be nice, only
     234             :         //      that would require loading in the correct order...
     235             :         //      (see dlopen() call below)
     236             :         //
     237             : 
     238             :         // load the plugin; the plugin will register itself
     239             :         //
     240             :         // use some really ugly globals because dlopen() does not give us
     241             :         // a way to pass parameters to the plugin factory constructor
     242             :         //
     243           0 :         g_next_register_name = name;
     244           0 :         g_next_register_filename = filename;
     245           0 :         g_next_register_introducer = introducer;
     246           0 :         void const * const h(dlopen(filename.toUtf8().data(), RTLD_LAZY | RTLD_GLOBAL));
     247           0 :         if(h == nullptr)
     248             :         {
     249           0 :             int const e(errno);
     250           0 :             SNAP_LOG_ERROR("error: cannot load plugin file \"")(filename)("\" (errno: ")(e)(", ")(dlerror())(")");
     251           0 :             good = false;
     252           0 :             continue;
     253             :         }
     254           0 :         g_next_register_name.clear();
     255           0 :         g_next_register_filename.clear();
     256           0 :         g_next_register_introducer.clear();
     257             : //SNAP_LOG_ERROR("note: registering plugin: \"")(name)("\"");
     258             :     }
     259             : 
     260             :     // set the g_ordered_plugins with the default order as alphabetical,
     261             :     // although we check dependencies to properly reorder as expected
     262             :     // by what each plugin tells us what its dependencies are
     263             :     //
     264             :     // TODO: refactor this embedded raw loop to take advantage of
     265             :     // C++11's lambda functions. Specifically, we should use std::find_if(),
     266             :     // and remove the goto + label. My suggested code is in the #if 0 block
     267             :     // below.
     268             :     //
     269             :     // Alexis Wilke, [07.06.17 14:46]
     270             :     // "If you can guarantee that the order won't be affected, we can do it
     271             :     // either way. It's sorted alphabetically, then by priority. The priority
     272             :     // has precedence over the alphabetical order. But we always end up with a
     273             :     // strong order (i.e. if the priority and names do not change plugin A will
     274             :     // always be before plugin B if it were before on the previous load)."
     275             :     //
     276           0 :     for(auto const & p : g_plugins)
     277             :     {
     278           0 :         QString const column_name(QString("|%1|").arg(p->get_plugin_name()));
     279             : #if 0
     280             :         // TODO: test this new code out to remove the raw loop below...
     281             :         auto found_iter = std::find_if( std::begin(g_ordered_plugins), std::end(g_ordered_plugins),
     282             :                 [&column_name]( auto const & plugin )
     283             :                 {
     284             :                     return plugin->dependencies().indexOf(column_name) >= 0;
     285             :                 });
     286             :         if( found_iter != std::end(g_ordered_plugins) )
     287             :         {
     288             :             g_ordered_plugins.insert( found_iter, p );
     289             :         }
     290             :         else
     291             :         {
     292             :             g_ordered_plugins.push_back(p);
     293             :         }
     294             : #else
     295           0 :         for(plugin_vector_t::iterator sp(g_ordered_plugins.begin());
     296           0 :                                       sp != g_ordered_plugins.end();
     297             :                                       ++sp)
     298             :         {
     299           0 :             if((*sp)->dependencies().indexOf(column_name) >= 0)
     300             :             {
     301           0 :                 g_ordered_plugins.insert(sp, p);
     302           0 :                 goto inserted;
     303             :             }
     304             :         }
     305             :         // if not before another plugin, insert at the end by default
     306           0 :         g_ordered_plugins.push_back(p);
     307           0 : inserted:;
     308             : #endif
     309             :     }
     310             : 
     311             :     // bootstrap() functions have to be called in order to get all the
     312             :     // signals registered in order! (YES!!! This one for() loop makes
     313             :     // all the signals work as expected by making sure they are in a
     314             :     // very specific order)
     315             :     //
     316           0 :     for(auto const & p : g_ordered_plugins)
     317             :     {
     318           0 :         p->bootstrap(snap);
     319             :     }
     320             : 
     321           0 :     return good;
     322             : }
     323             : 
     324             : 
     325             : /** \brief Try to find the plugin using the list of paths.
     326             :  *
     327             :  * This function searches for a plugin in each one of the specified
     328             :  * paths and as:
     329             :  *
     330             :  * \code
     331             :  *    <path>/<name>.so
     332             :  *    <path>/lib<name>.so
     333             :  *    <path>/<name>/<name>.so
     334             :  *    <path>/<name>/lib<name>.so
     335             :  * \endcode
     336             :  *
     337             :  * \todo
     338             :  * We may change the naming convention to make use of the ${PROJECT_NAME}
     339             :  * in the CMakeLists.txt files. In that case we'd end up with names
     340             :  * that include the work plugin as in:
     341             :  *
     342             :  * \code
     343             :  *    <path>/libplugin_<name>.so
     344             :  * \endcode
     345             :  *
     346             :  * \param[in] plugin_paths  The list of paths to check with.
     347             :  * \param[in] name  The name of the plugin being searched.
     348             :  *
     349             :  * \return The full path and filename of the plugin or empty if not found.
     350             :  */
     351           0 : QString find_plugin_filename(snap_string_list const & plugin_paths, QString const & name)
     352             : {
     353           0 :     int const max_paths(plugin_paths.size());
     354           0 :     for(int i(0); i < max_paths; ++i)
     355             :     {
     356           0 :         QString const path(plugin_paths[i]);
     357           0 :         QString filename(QString("%1/%2.so").arg(path).arg(name));
     358           0 :         if(QFile::exists(filename))
     359             :         {
     360           0 :             return filename;
     361             :         }
     362             : 
     363             :         //
     364             :         // If not found, see if it has a "lib" at the front of the file:
     365             :         //
     366           0 :         filename = QString("%1/lib%2.so").arg(path).arg(name);
     367           0 :         if(QFile::exists(filename))
     368             :         {
     369           0 :             return filename;
     370             :         }
     371             : 
     372             :         //
     373             :         // If not found, see if it lives under a named folder:
     374             :         //
     375           0 :         filename = QString("%1/%2/%2.so").arg(path).arg(name);
     376           0 :         if(QFile::exists(filename))
     377             :         {
     378           0 :             return filename;
     379             :         }
     380             : 
     381             :         //
     382             :         // Last test: check plugin names starting with "lib" in named folder:
     383             :         //
     384           0 :         filename = QString("%1/%2/lib%2.so").arg(path).arg(name);
     385           0 :         if(QFile::exists(filename))
     386             :         {
     387           0 :             return filename;
     388             :         }
     389             :     }
     390             : 
     391           0 :     return QString();
     392             : }
     393             : 
     394             : 
     395             : /** \brief Verify that a name is a valid plugin name.
     396             :  *
     397             :  * This function checks a string to know whether it is a valid plugin name.
     398             :  *
     399             :  * A valid plugin name is a string of letters (A-Z or a-z), digits (0-9),
     400             :  * and the underscore (_), dash (-), and period (.). Although the name
     401             :  * cannot start or end with a dash or a period.
     402             :  *
     403             :  * \todo
     404             :  * At this time this function prints errors out in stderr. This may
     405             :  * change later and errors will be sent to the logger. However, we
     406             :  * need to verify that the logger is ready when this function gets
     407             :  * called.
     408             :  *
     409             :  * \param[in] name  The name to verify.
     410             :  *
     411             :  * \return true if the name is considered valid.
     412             :  */
     413           0 : bool verify_plugin_name(QString const & name)
     414             : {
     415           0 :     if(name.isEmpty())
     416             :     {
     417           0 :         SNAP_LOG_ERROR() << "error: an empty plugin name is not valid.";
     418           0 :         return false;
     419             :     }
     420           0 :     for(QString::const_iterator p(name.begin()); p != name.end(); ++p)
     421             :     {
     422           0 :         if((*p < 'a' || *p > 'z')
     423           0 :         && (*p < 'A' || *p > 'Z')
     424           0 :         && (*p < '0' || *p > '9')
     425           0 :         && *p != '_' && *p != '-' && *p != '.')
     426             :         {
     427           0 :             SNAP_LOG_ERROR("error: plugin name \"")(name)("\" includes forbidden characters.");
     428           0 :             return false;
     429             :         }
     430             :     }
     431             :     // Note: we know that name is not empty
     432           0 :     QChar const first(name[0]);
     433           0 :     if(first == '.'
     434           0 :     || first == '-'
     435           0 :     || (first >= '0' && first <= '9'))
     436             :     {
     437           0 :         SNAP_LOG_ERROR("error: plugin name \"")(name)("\" cannot start with a digit (0-9), a period (.), or dash (-).");
     438           0 :         return false;
     439             :     }
     440             :     // Note: we know that name is not empty
     441           0 :     QChar const last(name[name.length() - 1]);
     442           0 :     if(last == '.' || last == '-')
     443             :     {
     444           0 :         SNAP_LOG_ERROR("error: plugin name \"")(name)("\" cannot end with a period (.) or dash (-).");
     445           0 :         return false;
     446             :     }
     447             : 
     448           0 :     return true;
     449             : }
     450             : 
     451             : 
     452             : /** \brief Check whether a plugin was loaded.
     453             :  *
     454             :  * This function searches the list of loaded plugins and returns true if the
     455             :  * plugin with the speficied \p name exists.
     456             :  *
     457             :  * \param[in] name  The name of the plugin to check for.
     458             :  *
     459             :  * \return true if the plugin is loaded, false otherwise.
     460             :  */
     461           0 : bool exists(QString const & name)
     462             : {
     463           0 :     return g_plugins.contains(name);
     464             : }
     465             : 
     466             : 
     467             : /** \brief Register a plugin in the list of plugins.
     468             :  *
     469             :  * This function is called by plugin factories to register new plugins.
     470             :  * Do not attempt to call this function directly or you'll get an
     471             :  * exception.
     472             :  *
     473             :  * \exception plugin_exception
     474             :  * If the name is empty, the name does not correspond to the plugin
     475             :  * being loaded, or the plugin is being loaded for the second time,
     476             :  * then this exception is raised.
     477             :  *
     478             :  * \param[in] name  The name of the plugin being added.
     479             :  * \param[in] p  A pointer to the plugin being added.
     480             :  */
     481           0 : void register_plugin(QString const & name, plugin * p)
     482             : {
     483           0 :     if(name.isEmpty())
     484             :     {
     485           0 :         throw plugin_exception("plugin name missing when registering... expected \"" + name + "\".");
     486             :     }
     487             : 
     488           0 :     QString const full_name(g_next_register_introducer.isEmpty()
     489             :                                 ? name
     490           0 :                                 : g_next_register_introducer + "_" + name);
     491             : 
     492           0 :     if(full_name != g_next_register_name)
     493             :     {
     494           0 :         throw plugin_exception("it is not possible to register a plugin (" + full_name + ") other than the one being loaded (" + g_next_register_name + ").");
     495             :     }
     496             : 
     497             : #ifdef DEBUG
     498             :     // this is not possible if you use the macro, but in case you create
     499             :     // your own factory instance by hand, it is a requirement too
     500             :     //
     501           0 :     if(full_name != p->get_plugin_name())
     502             :     {
     503           0 :         throw plugin_exception("somehow your plugin factory name is \"" + p->get_plugin_name() + "\" when we were expecting \"" + name + "\".");
     504             :     }
     505             : #endif
     506           0 :     if(exists(name))
     507             :     {
     508             :         // this should not happen except if the plugin factory was attempting
     509             :         // to register the same plugin many times in a row
     510             :         //
     511           0 :         throw plugin_exception("it is not possible to register a plugin more than once (" + name + ").");
     512             :     }
     513             : 
     514             :     // verify the server version the plugin was compiled with and this
     515             :     // server version
     516             :     //
     517             :     // TODO: should we not check the patch version? (i.e. assume that if only
     518             :     //       the patch changes then the interface has not changed.)
     519             :     //
     520             :     // TODO: the #ifndef _DEBUG comes from the fact that SNAP-54 is not
     521             :     //       compatible with debug, see SNAP-416 for additional details
     522             :     //       about the versioning problem in the development environment
     523             :     //
     524             : #ifndef _DEBUG
     525             :     if(p->get_server_major_version() != SNAPWEBSITES_VERSION_MAJOR
     526             :     || p->get_server_minor_version() != SNAPWEBSITES_VERSION_MINOR
     527             :     //|| p->get_server_patch_version() != SNAPWEBSITES_VERSION_PATCH -- for now, ignore the patch to avoid some potential problems
     528             :     )
     529             :     {
     530             :         // a mixed up plugin is like to cause problems so prevent the loading
     531             :         // of a plugin which does not correspond one to one to its server
     532             :         // version
     533             :         //
     534             :         // I ran in a problem with building finball and thus could not jump
     535             :         // from 1.6.x to 1.7.x (pbuilder would not install some packages
     536             :         // because of invalid versions.) So for now we just want to emit an
     537             :         // error. We'll have to see later how we want to deal with this
     538             :         // problem.
     539             :         //
     540             :         SNAP_LOG_ERROR(QString("incompatible server versions between this server (%1.%2.%3) and this plugin \"%7\" (%4.%5.%6) -- Note: we ignore the patch version")
     541             :                     .arg(SNAPWEBSITES_VERSION_MAJOR)
     542             :                     .arg(SNAPWEBSITES_VERSION_MINOR)
     543             :                     .arg(SNAPWEBSITES_VERSION_PATCH)
     544             :                     .arg(p->get_server_major_version())
     545             :                     .arg(p->get_server_minor_version())
     546             :                     .arg(p->get_server_patch_version())
     547             :                     .arg(name)
     548             :                     );
     549             :         //throw plugin_exception(QString("incompatible server versions between this server (%1.%2.%3) and plugin \"%7\" (%4.%5.%6) -- Note: we ignore the patch version")
     550             :         //            .arg(SNAPWEBSITES_VERSION_MAJOR)
     551             :         //            .arg(SNAPWEBSITES_VERSION_MINOR)
     552             :         //            .arg(SNAPWEBSITES_VERSION_PATCH)
     553             :         //            .arg(p->get_server_major_version())
     554             :         //            .arg(p->get_server_minor_version())
     555             :         //            .arg(p->get_server_patch_version())
     556             :         //            .arg(name)
     557             :         //            );
     558             :     }
     559             : #endif
     560             : 
     561           0 :     g_plugins.insert(name, p);
     562           0 : }
     563             : 
     564             : 
     565             : /** \brief Initialize a plugin.
     566             :  *
     567             :  * This function initializes the plugin with its filename.
     568             :  */
     569           0 : plugin::plugin()
     570             :     : f_name(g_next_register_name)
     571           0 :     , f_filename(g_next_register_filename)
     572             :     //, f_last_modification(0) -- auto-init
     573             :     //, f_version_major(0) -- auto-init
     574             :     //, f_version_minor(0) -- auto-init
     575             : {
     576           0 : }
     577             : 
     578             : 
     579             : /** \brief Define the version of the plugin.
     580             :  *
     581             :  * This function saves the version of the plugin in the object.
     582             :  * This way other systems can access the version.
     583             :  *
     584             :  * In general you never call that function. It is automatically
     585             :  * called by the SNAP_PLUGIN_START() macro. Note that the
     586             :  * function cannot be called more than once and the version
     587             :  * cannot be zero or negative.
     588             :  *
     589             :  * \param[in] version_major  The major version of the plugin.
     590             :  * \param[in] version_minor  The minor version of the plugin.
     591             :  */
     592           0 : void plugin::set_version(int version_major, int version_minor)
     593             : {
     594           0 :     if(f_version_major != 0
     595           0 :     || f_version_minor != 0)
     596             :     {
     597             :         // version was already defined; it cannot be set again
     598           0 :         throw plugin_exception(QString("version of plugin \"%1\" already defined.").arg(f_name));
     599             :     }
     600             : 
     601           0 :     if(version_major < 0
     602           0 :     || version_minor < 0
     603           0 :     || (version_major == 0 && version_minor == 0))
     604             :     {
     605             :         // version cannot be negative or null
     606           0 :         throw plugin_exception(QString("version of plugin \"%1\" cannot be zero or negative (%2.%3).")
     607           0 :                     .arg(f_name).arg(version_major).arg(version_minor));
     608             :     }
     609             : 
     610           0 :     f_version_major = version_major;
     611           0 :     f_version_minor = version_minor;
     612           0 : }
     613             : 
     614             : 
     615             : /** \brief Define the server version the plugin was compiled against.
     616             :  *
     617             :  * This function saves the server version of the plugin in the plugin object.
     618             :  * This way the registration function can verify that the plugin was compiled
     619             :  * with a compatible server.
     620             :  *
     621             :  * In general you never call that function. It is automatically
     622             :  * called by the SNAP_PLUGIN_START() macro. Note that the
     623             :  * function cannot be called more than once and the version
     624             :  * cannot be zero or negative.
     625             :  *
     626             :  * \param[in] version_major  The major version of the server.
     627             :  * \param[in] version_minor  The minor version of the server.
     628             :  * \param[in] version_patch  The patch version of the server.
     629             :  */
     630           0 : void plugin::set_server_version(int version_major, int version_minor, int version_patch)
     631             : {
     632           0 :     if(f_server_version_major != 0
     633           0 :     || f_server_version_minor != 0
     634           0 :     || f_server_version_patch != 0)
     635             :     {
     636             :         // server version was already defined; it cannot be set again
     637             :         //
     638           0 :         throw plugin_exception(QString("server version of plugin \"%1\" already defined.").arg(f_name));
     639             :     }
     640             : 
     641           0 :     if(version_major < 0
     642           0 :     || version_minor < 0
     643           0 :     || version_patch < 0
     644           0 :     || (version_major == 0 && version_minor == 0 && version_patch == 0))
     645             :     {
     646             :         // server version cannot be negative or null
     647             :         //
     648           0 :         throw plugin_exception(QString("server version of plugin \"%1\" cannot be zero or negative (%2.%3.%4).")
     649           0 :                     .arg(f_name)
     650           0 :                     .arg(version_major)
     651           0 :                     .arg(version_minor)
     652           0 :                     .arg(version_patch));
     653             :     }
     654             : 
     655           0 :     f_server_version_major = version_major;
     656           0 :     f_server_version_minor = version_minor;
     657           0 :     f_server_version_patch = version_patch;
     658           0 : }
     659             : 
     660             : 
     661             : /** \brief Retrieve the major version of this plugin.
     662             :  *
     663             :  * This function returns the major version of this plugin. This is the
     664             :  * same version as defined in the plugin factory.
     665             :  *
     666             :  * \return The major version of the plugin.
     667             :  */
     668           0 : int plugin::get_major_version() const
     669             : {
     670           0 :     return f_version_major;
     671             : }
     672             : 
     673             : 
     674             : /** \brief Retrieve the minor version of this plugin.
     675             :  *
     676             :  * This function returns the minor version of this plugin. This is the
     677             :  * same version as defined in the plugin factory.
     678             :  *
     679             :  * \return The minor version of the plugin.
     680             :  */
     681           0 : int plugin::get_minor_version() const
     682             : {
     683           0 :     return f_version_minor;
     684             : }
     685             : 
     686             : 
     687             : /** \brief Retrieve the major version of the server of this plugin.
     688             :  *
     689             :  * This function returns the major version of the server this plugin was
     690             :  * compiled against. This is the version from the libsnapwebsites version.h
     691             :  * file.
     692             :  *
     693             :  * \return The minor version of the server of this plugin.
     694             :  */
     695           0 : int plugin::get_server_major_version() const
     696             : {
     697           0 :     return f_server_version_major;
     698             : }
     699             : 
     700             : 
     701             : /** \brief Retrieve the minor version of the server of this plugin.
     702             :  *
     703             :  * This function returns the minor version of the server this plugin was
     704             :  * compiled against. This is the version from the libsnapwebsites version.h
     705             :  * file.
     706             :  *
     707             :  * \return The minor version of the server of this plugin.
     708             :  */
     709           0 : int plugin::get_server_minor_version() const
     710             : {
     711           0 :     return f_server_version_minor;
     712             : }
     713             : 
     714             : 
     715             : /** \brief Retrieve the patch version of the server of this plugin.
     716             :  *
     717             :  * This function returns the patch version of the server this plugin was
     718             :  * compiled against. This is the version from the libsnapwebsites version.h
     719             :  * file.
     720             :  *
     721             :  * \return The patch version of the server of this plugin.
     722             :  */
     723           0 : int plugin::get_server_patch_version() const
     724             : {
     725           0 :     return f_server_version_patch;
     726             : }
     727             : 
     728             : 
     729             : /** \brief Retrieve the name of the plugin as defined on creation.
     730             :  *
     731             :  * This function returns the name of the plugin as defined on
     732             :  * creation of the plugin. It is not possible to modify the
     733             :  * name for safety.
     734             :  *
     735             :  * \return The name of the plugin as defined in your SNAP_PLUGIN_START() instantiation.
     736             :  */
     737           0 : QString plugin::get_plugin_name() const
     738             : {
     739           0 :     return f_name;
     740             : }
     741             : 
     742             : 
     743             : /** \brief Get the last modification date of the plugin.
     744             :  *
     745             :  * This function reads the modification date on the plugin file to determine
     746             :  * when it was last modified. This date can be used to determine whether the
     747             :  * plugin was modified since the last time we ran snap with this website.
     748             :  *
     749             :  * \return The last modification date and time in micro seconds.
     750             :  */
     751           0 : int64_t plugin::last_modification() const
     752             : {
     753           0 :     if(0 == f_last_modification)
     754             :     {
     755             :         // read the info only once
     756             :         struct stat s;
     757           0 :         if(stat(f_filename.toUtf8().data(), &s) == 0)
     758             :         {
     759             :             // should we make use of the usec mtime when available?
     760           0 :             f_last_modification = static_cast<int64_t>(s.st_mtime * 1000000LL);
     761             :         }
     762             :         // else TBD: should we throw here?
     763             :     }
     764             : 
     765           0 :     return f_last_modification;
     766             : }
     767             : 
     768             : 
     769             : /** \brief Return the URL to an icon representing your plugin.
     770             :  *
     771             :  * Each plugin can be assigned an icon. The path to that icon has
     772             :  * to be returned by this function. It us used whenever we build
     773             :  * lists representing plugins.
     774             :  *
     775             :  * The default function returns the path to a default plugin image.
     776             :  *
     777             :  * The image must be a 64x64 picture. The CSS will enforce the size
     778             :  * so it will possibly get stretched in weird ways if you did not
     779             :  * use the correct size to start withicon.
     780             :  *
     781             :  * \return The path to the default plugin icon.
     782             :  */
     783           0 : QString plugin::icon() const
     784             : {
     785           0 :     return "/images/snap/plugin-icon-64x64.png";
     786             : }
     787             : 
     788             : 
     789             : /** \fn QString plugin::description() const;
     790             :  * \brief Return a string describing this plugin.
     791             :  *
     792             :  * This function must be overloaded. It is expected to return a description
     793             :  * of the plugin. The string may include HTML. It should be limited to
     794             :  * inline HTML tags, although it can include header tags and paragraphs
     795             :  * too.
     796             :  *
     797             :  * The description should be relatively brief. The plugins also offer a
     798             :  * help URI which sends users to our snapwebsites.org website (or the
     799             :  * author of the plugin wbesite) so we do not need to go crazy in the
     800             :  * description. That external help page can go at length.
     801             :  *
     802             :  * \return A string representing the plugin description.
     803             :  */
     804             : 
     805             : 
     806             : /** \brief Comma separated list of tags.
     807             :  *
     808             :  * This function returns a list of comma separated tags. It is used
     809             :  * to categorize a plugin so that way it makes it easier to find
     810             :  * in the large list presented to users under the Plugin Selector.
     811             :  *
     812             :  * For example, plugins that are used to test other plugins and
     813             :  * other parts of the system can be assigned the tag "test".
     814             :  *
     815             :  * By default plugins are not assigned any tags. This means they
     816             :  * will appear under the "others" special tag. You can always
     817             :  * make this explicit or offer the "others" tag along with
     818             :  * more useful tags, for example: "image,others".
     819             :  *
     820             :  * \return A list of tags, comma separated.
     821             :  */
     822           0 : QString plugin::plugin_categorization_tags() const
     823             : {
     824           0 :     return "";
     825             : }
     826             : 
     827             : 
     828             : /** \brief Return the URI to the help page for this plugin.
     829             :  *
     830             :  * Each plugin can be assigned a help page. By default, we
     831             :  * define the URL as:
     832             :  *
     833             :  * \code
     834             :  *      https://snapwebsites.org/help/plugin/<plugin-name>
     835             :  * \endcode
     836             :  *
     837             :  * If you program your own plugin, you are expected to overload
     838             :  * this function and send users to your own website.
     839             :  *
     840             :  * \return The URI to the help page for this plugin.
     841             :  */
     842           0 : QString plugin::help_uri() const
     843             : {
     844           0 :     return QString("https://snapwebsites.org/help/plugin/%1").arg(f_name);
     845             : }
     846             : 
     847             : 
     848             : /** \brief Return the path to the settings page for this plugin.
     849             :  *
     850             :  * Each plugin can be assigned a settings page. By default, this
     851             :  * function returns an empty string meaning that no settings are
     852             :  * available.
     853             :  *
     854             :  * The function has to return an absolute path to a site borne
     855             :  * page that will allow an administrator to change various
     856             :  * settings that this plugin is handling.
     857             :  *
     858             :  * Some plugins may not themelves offer settings, but they may
     859             :  * still return a settings path to another plugin that is setup
     860             :  * to handle their settings (i.e. the info plugin handles many
     861             :  * other lower level plugins settings.)
     862             :  *
     863             :  * By default the function returns an empty path, meaning that the
     864             :  * settings button needs to be disabled in the plugin selector.
     865             :  *
     866             :  * \return The path to the settings page for this plugin.
     867             :  */
     868           0 : QString plugin::settings_path() const
     869             : {
     870           0 :     return QString();
     871             : }
     872             : 
     873             : 
     874             : /** \fn QString plugin::dependencies() const;
     875             :  * \brief Return a list of required dependencies.
     876             :  *
     877             :  * This function returns a list of dependencies, plugin names written
     878             :  * between pipes (|). All plugins have at least one dependency since
     879             :  * most plugins will not work without the base plugin (i.e. "|server|"
     880             :  * is the bottom most base you can use in your plugin).
     881             :  *
     882             :  * At this time, the "content" and "test_plugin_suite" plugins have no
     883             :  * dependencies.
     884             :  *
     885             :  * \note
     886             :  * Until "links" is merged with "content", it will depend on "content"
     887             :  * so that way "links" signals are registered after "content" signals.
     888             :  *
     889             :  * \return A list of plugin names representing all dependencies.
     890             :  */
     891             : 
     892             : 
     893             : /** \fn void plugin::bootstrap(snap_child * snap)
     894             :  * \brief Bootstrap this plugin.
     895             :  *
     896             :  * The bootstrap virtual function is used to initialize the plugins. At
     897             :  * this point all the plugins are loaded, however, they are not yet
     898             :  * ready to receive signals because all plugins are not yet connected.
     899             :  * The bootstrap() function is actually used to get all the listeners
     900             :  * registered.
     901             :  *
     902             :  * Note that the plugin implementation loads all the plugins, sorts them,
     903             :  * then calls their bootstrap() function. Afterward, the init() function
     904             :  * is likely called. The bootstrap() registers signals and the server
     905             :  * init() signal can be used to send signals since at that point all the
     906             :  * plugins are properly installed and have all of their signals registered.
     907             :  *
     908             :  * \note
     909             :  * This is a pure virtual which is not implemented here so that way your
     910             :  * plugin will crash the server if you did not implement this function
     911             :  * which is considered mandatory.
     912             :  *
     913             :  * \param[in,out] snap  The snap child process.
     914             :  */
     915             : 
     916             : 
     917             : /** \brief Run an update.
     918             :  *
     919             :  * This function is a stub that does nothing. It is here so any plug in that
     920             :  * does not need an update does not need to define an "empty" function.
     921             :  *
     922             :  * At this time the function ignores the \p last_updated parameter and 
     923             :  * it always returns the same date: Jan 1, 1990 at 00:00:00.
     924             :  *
     925             :  * \param[in] last_updated  The UTC Unix date when this plugin was last updated (in micro seconds).
     926             :  *
     927             :  * \return The UTC Unix date of the last update of this plugin.
     928             :  */
     929           0 : int64_t plugin::do_update(int64_t last_updated)
     930             : {
     931           0 :     NOTUSED(last_updated);
     932             : 
     933           0 :     SNAP_PLUGIN_UPDATE_INIT();
     934             : 
     935             :     // in a complete implementation you have entries like this one:
     936             :     //SNAP_PLUGIN_UPDATE(2012, 1, 1, 0, 0, 0, initial_update);
     937             : 
     938           0 :     SNAP_PLUGIN_UPDATE_EXIT();
     939             : }
     940             : 
     941             : 
     942             : /** \brief Run a dynamic update.
     943             :  *
     944             :  * This function is called after the do_update(). This very version is
     945             :  * a stub that does nothing. It can be overloaded to create content in
     946             :  * the database after the content.xml was installed fully. In other
     947             :  * words, the dynamic update can make use of data that the content.xml
     948             :  * will be adding ahead of time.
     949             :  *
     950             :  * At this time the function ignores the \p last_updated parameter and 
     951             :  * it always returns the same date: Jan 1, 1990 at 00:00:00.
     952             :  *
     953             :  * \param[in] last_updated  The UTC Unix date when this plugin was last updated (in micro seconds).
     954             :  *
     955             :  * \return The UTC Unix date of the last update of this plugin.
     956             :  */
     957           0 : int64_t plugin::do_dynamic_update(int64_t last_updated)
     958             : {
     959           0 :     NOTUSED(last_updated);
     960             : 
     961           0 :     SNAP_PLUGIN_UPDATE_INIT();
     962             : 
     963             :     // in a complete implementation you have entries like this one:
     964             :     //SNAP_PLUGIN_UPDATE(2012, 1, 1, 0, 0, 0, dynamic_update);
     965             : 
     966           0 :     SNAP_PLUGIN_UPDATE_EXIT();
     967             : }
     968             : 
     969             : 
     970             : /** \brief Retrieve a pointer to an existing plugin.
     971             :  *
     972             :  * This function returns a pointer to a plugin that was previously loaded
     973             :  * with the load() function. If you only need to test whether a plugin
     974             :  * exists, then you should use exists() instead.
     975             :  *
     976             :  * \note
     977             :  * This function should not be called until your plugin bootstrap() function
     978             :  * is called. Before then, there are no guarantees that the plugin was already
     979             :  * loaded.
     980             :  *
     981             :  * \param[in] name  The name of the plugin to retrieve.
     982             :  *
     983             :  * \return This function returns a pointer to the named plugin, or nullptr
     984             :  * if the plugin was not loaded.
     985             :  *
     986             :  * \sa load()
     987             :  * \sa exists()
     988             :  */
     989           0 : plugin * get_plugin(QString const & name)
     990             : {
     991           0 :     return g_plugins.value(name, nullptr);
     992             : }
     993             : 
     994             : 
     995             : /** \brief Retrieve the list of plugins.
     996             :  *
     997             :  * This function returns the list of plugins that were loaded in this
     998             :  * session. Remember that plugins are loaded each time a client accesses
     999             :  * the server.
    1000             :  *
    1001             :  * This means that the list is complete only once you are in the snap
    1002             :  * child and after the plugins were initialized. If you are in a plugin,
    1003             :  * this means the list is not complete in the constructor. It is complete
    1004             :  * anywhere else.
    1005             :  *
    1006             :  * \return List of plugins in a map indexed by plugin name
    1007             :  *         (i.e. alphabetical order).
    1008             :  */
    1009           0 : plugin_map_t const & get_plugin_list()
    1010             : {
    1011           0 :     return g_plugins;
    1012             : }
    1013             : 
    1014             : 
    1015             : /** \brief Retrieve the list of plugins.
    1016             :  *
    1017             :  * This function returns the list of plugins that were sorted, once
    1018             :  * loaded, using their dependencies. This is a vector since we need
    1019             :  * to keep a very specific order of the plugins.
    1020             :  *
    1021             :  * This list is empty until all the plugins were loaded.
    1022             :  *
    1023             :  * This list should be empty when your plugins constructors are called.
    1024             :  *
    1025             :  * \return The sorted list of plugins in a vector.
    1026             :  */
    1027           0 : plugin_vector_t const & get_plugin_vector()
    1028             : {
    1029           0 :     return g_ordered_plugins;
    1030             : }
    1031             : 
    1032             : 
    1033             : /** \brief Read a plugin information.
    1034             :  *
    1035             :  * This constructor reads all the available information from the named
    1036             :  * plugin.
    1037             :  */
    1038           0 : plugin_info::plugin_info(QString const & plugin_paths, QString const & name)
    1039             : {
    1040           0 :     if(name == "server")
    1041             :     {
    1042             :         // this is a special case, the user is requesting information about
    1043             :         // the snapserver (snapwebsites.cpp) and not a plugin per-se.
    1044             :         //
    1045           0 :         f_name =                name;
    1046           0 :         f_filename =            "snapserver";
    1047           0 :         f_last_modification =   0;
    1048           0 :         f_icon =                "/images/snap/snap-logo-64x64.png";
    1049           0 :         f_description =         "The Snap! Websites server defines the base plugin used by the snap system.";
    1050           0 :         f_categorization_tags = "core";
    1051           0 :         f_help_uri =            "https://snapwebsites.org/help/plugin/server";
    1052           0 :         f_settings_path =       "/admin/plugins";
    1053           0 :         f_dependencies =        "";
    1054           0 :         f_version_major =       SNAPWEBSITES_VERSION_MAJOR;
    1055           0 :         f_version_minor =       SNAPWEBSITES_VERSION_MINOR;
    1056             : 
    1057             :         // too bad that dlopen() returns opaque handles only...
    1058             :         // that being said, we can still walk the walk and
    1059             :         // find the full path to the currently running binary
    1060             :         //
    1061             :         struct unknown_structures  // <- yuck
    1062             :         {
    1063             :             void *                      f_unused[3];
    1064             :             struct unknown_structures * f_pointer;
    1065             :         };
    1066           0 :         struct unknown_structures * p(reinterpret_cast<struct unknown_structures *>(dlopen(nullptr, RTLD_LAZY | RTLD_GLOBAL)));
    1067           0 :         if(p != nullptr)
    1068             :         {
    1069           0 :             p = p->f_pointer;
    1070           0 :             if(p != nullptr)
    1071             :             {
    1072           0 :                 struct link_map * map(reinterpret_cast<struct link_map *>(p->f_pointer));
    1073           0 :                 if(map != nullptr)
    1074             :                 {
    1075           0 :                     if(map->l_name != nullptr)
    1076             :                     {
    1077             :                         // full path to the snapwebsites.so library
    1078           0 :                         f_filename = QString::fromUtf8(map->l_name);
    1079             : 
    1080           0 :                         if(!f_filename.isEmpty())
    1081             :                         {
    1082             :                             struct stat s;
    1083           0 :                             if(stat(f_filename.toUtf8().data(), &s) == 0)
    1084             :                             {
    1085             :                                 // should we make use of the usec mtime when available?
    1086           0 :                                 f_last_modification = static_cast<int64_t>(s.st_mtime * 1000000LL);
    1087             :                             }
    1088             :                         }
    1089             :                     }
    1090             :                 }
    1091             :             }
    1092             :         }
    1093           0 :         return;
    1094             :     }
    1095             : 
    1096           0 :     snap_string_list const paths(plugin_paths.split(':'));
    1097           0 :     QString const filename(find_plugin_filename(paths, name));
    1098           0 :     if(filename.isEmpty())
    1099             :     {
    1100           0 :         throw plugin_exception(QString("plugin named \"%1\" not found.").arg(name));
    1101             :     }
    1102             : 
    1103             :     // "normal" load of the plugin... (We do not really have a choice)
    1104             :     //
    1105             :     // Note that this is the normal low level load, that means the plugin
    1106             :     // will not get its bootstrap() and other initialization functions
    1107             :     // called... we will be limited to a very small number of functions.
    1108             :     //
    1109             :     // XXX: allow for the non-adding to the global list?
    1110             :     //      in general we should be fine since their signals will not
    1111             :     //      be installed, however, the plugin_exists() function will
    1112             :     //      return 'true', which is not correct
    1113             :     //
    1114           0 :     g_next_register_name = name;
    1115           0 :     g_next_register_filename = filename;
    1116           0 :     void const * const h(dlopen(filename.toUtf8().data(), RTLD_LAZY | RTLD_GLOBAL));
    1117           0 :     if(h == nullptr)
    1118             :     {
    1119           0 :         int const e(errno);
    1120           0 :         std::stringstream ss;
    1121           0 :         ss << "error: cannot load plugin file \"" << filename << "\" (errno: " << e << ", " << dlerror() << ")";
    1122           0 :         throw plugin_exception(ss.str());
    1123             :     }
    1124           0 :     g_next_register_name.clear();
    1125           0 :     g_next_register_filename.clear();
    1126             : 
    1127           0 :     plugin * p(get_plugin(name));
    1128           0 :     if(p == nullptr)
    1129             :     {
    1130           0 :         std::stringstream ss;
    1131           0 :         ss << "error: cannot find plugin \"" << name << "\", even though the loading was successful.";
    1132           0 :         throw plugin_exception(ss.str());
    1133             :     }
    1134             : 
    1135           0 :     f_name =                name;
    1136           0 :     f_filename =            filename;
    1137           0 :     f_last_modification =   p->last_modification();
    1138           0 :     f_icon =                p->icon();
    1139           0 :     f_description =         p->description();
    1140           0 :     f_categorization_tags = p->plugin_categorization_tags();
    1141           0 :     f_help_uri =            p->help_uri();
    1142           0 :     f_settings_path =       p->settings_path();
    1143           0 :     f_dependencies =        p->dependencies();
    1144           0 :     f_version_major =       p->get_major_version();
    1145           0 :     f_version_minor =       p->get_minor_version();
    1146             : }
    1147             : 
    1148             : 
    1149             : /** \brief Retrieve the name of the plugin.
    1150             :  *
    1151             :  * This function returns the name of this plugin.
    1152             :  *
    1153             :  * \return The name of this plugin.
    1154             :  */
    1155           0 : QString const & plugin_info::get_name() const
    1156             : {
    1157           0 :     return f_name;
    1158             : }
    1159             : 
    1160             : 
    1161             : /** \brief Get the filename of the plugin.
    1162             :  *
    1163             :  * Advanced informaton, the plugin path on the server. This is really
    1164             :  * only useful for developers and administrators to make sure things
    1165             :  * are in the right place.
    1166             :  *
    1167             :  * \return The path to this plugin.
    1168             :  */
    1169           0 : QString const & plugin_info::get_filename() const
    1170             : {
    1171           0 :     return f_filename;
    1172             : }
    1173             : 
    1174             : 
    1175             : /** \brief Get the last modification time of this plugin.
    1176             :  *
    1177             :  * This function reads the mtime in microseconds of the file attached
    1178             :  * to this plugin and returns that value.
    1179             :  *
    1180             :  * Assuming people do not temper with the modification of the file,
    1181             :  * this represents the last time the plugin was compiled and packaged.
    1182             :  *
    1183             :  * \return The time of the last modifications of this plugin in microseconds.
    1184             :  */
    1185           0 : int64_t plugin_info::get_last_modification() const
    1186             : {
    1187           0 :     return f_last_modification;
    1188             : }
    1189             : 
    1190             : 
    1191             : /** \brief Retrieve path to the icon.
    1192             :  *
    1193             :  * This function returns the local path to the icon representing this plugin.
    1194             :  *
    1195             :  * \return A path to the plugin icon.
    1196             :  */
    1197           0 : QString const & plugin_info::get_icon() const
    1198             : {
    1199           0 :     return f_icon;
    1200             : }
    1201             : 
    1202             : 
    1203             : /** \brief Retrieve URI to the help page for this plugin.
    1204             :  *
    1205             :  * This function returns the URI to the help page for this plugin.
    1206             :  * In most cases this is a URI to an external website that describes
    1207             :  * the plugin in details: what it does, what it does not do, what
    1208             :  * the settings are for, how to set it up in this way or that way, etc.
    1209             :  *
    1210             :  * \return The URI to the help page.
    1211             :  */
    1212           0 : QString const & plugin_info::get_help_uri() const
    1213             : {
    1214           0 :     return f_help_uri;
    1215             : }
    1216             : 
    1217             : 
    1218             : /** \brief Retrieve path to the settings page for this plugin.
    1219             :  *
    1220             :  * This function returns the path to the main settings of this plugin.
    1221             :  *
    1222             :  * \return The path to the settings page.
    1223             :  */
    1224           0 : QString const & plugin_info::get_settings_path() const
    1225             : {
    1226           0 :     return f_settings_path;
    1227             : }
    1228             : 
    1229             : 
    1230             : /** \brief Retrieve the plugin description.
    1231             :  *
    1232             :  * Each plugin has a small description defined in the code. This
    1233             :  * description is shown when you have to deal with the plugin
    1234             :  * in such places as the page allowing you to install / uninstall
    1235             :  * the plugin.
    1236             :  *
    1237             :  * \return The description of the plugin.
    1238             :  */
    1239           0 : QString const & plugin_info::get_description() const
    1240             : {
    1241           0 :     return f_description;
    1242             : }
    1243             : 
    1244             : 
    1245             : /** \brief Retrieve the plugin last of tags used for categorization.
    1246             :  *
    1247             :  * Each plugin can be given a set of tags, comma separated, to define
    1248             :  * its category. For example, core plugins are generally marked with
    1249             :  * the "core" tag. The "base" tag is generally used by the set of
    1250             :  * plugins that one cannot uninstall because otherwise the system
    1251             :  * would break. Etc.
    1252             :  *
    1253             :  * \return The list of comma separated tags.
    1254             :  */
    1255           0 : QString const & plugin_info::get_plugin_categorization_tags() const
    1256             : {
    1257           0 :     return f_categorization_tags;
    1258             : }
    1259             : 
    1260             : 
    1261             : /** \brief Retrieve the plugin dependencies.
    1262             :  *
    1263             :  * Most plugins have a set of dependencies which must be
    1264             :  * initialized before they themselves get initialized. This
    1265             :  * way they will receive events after those dependencies.
    1266             :  * In other words, we have a system which is well defined
    1267             :  * (deterministic in terms of who receives what and when.)
    1268             :  *
    1269             :  * This function returns the raw string defined in the plugins
    1270             :  * which means the list of plugin names are written between
    1271             :  * pipes (|) characters.
    1272             :  *
    1273             :  * \return The dependencies of the plugin.
    1274             :  */
    1275           0 : QString const & plugin_info::get_dependencies() const
    1276             : {
    1277           0 :     return f_dependencies;
    1278             : }
    1279             : 
    1280             : 
    1281             : /** \brief Retrieve the major version of the plugin.
    1282             :  *
    1283             :  * This function returns the major version of the plugin as a number.
    1284             :  *
    1285             :  * \return The major version of this plugin.
    1286             :  */
    1287           0 : int32_t plugin_info::get_version_major() const
    1288             : {
    1289           0 :     return f_version_major;
    1290             : }
    1291             : 
    1292             : 
    1293             : /** \brief Retrieve the minor version of the plugin.
    1294             :  *
    1295             :  * This function returns the minor version of the plugin as a number.
    1296             :  *
    1297             :  * \return The minor version of this plugin.
    1298             :  */
    1299           0 : int32_t plugin_info::get_version_minor() const
    1300             : {
    1301           0 :     return f_version_minor;
    1302             : }
    1303             : 
    1304             : 
    1305             : 
    1306             : 
    1307             : } // namespace plugins
    1308           6 : } // namespace snap
    1309             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13