Line data Source code
1 : // Snap Servers -- plugins loader
2 : // Copyright (c) 2011-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 : #pragma once
18 :
19 : #include "snapwebsites/snap_exception.h"
20 : #include "snapwebsites/snap_string_list.h"
21 :
22 : #include <memory>
23 :
24 : #include <QVector>
25 : #include <QMap>
26 :
27 :
28 : namespace snap
29 : {
30 :
31 : // bootstrap() references snap_child as a pointer
32 : class snap_child;
33 :
34 : namespace plugins
35 : {
36 :
37 0 : class plugin_exception : public snap_exception
38 : {
39 : public:
40 : explicit plugin_exception(char const * what_msg) : snap_exception("plugin", what_msg) {}
41 0 : explicit plugin_exception(std::string const & what_msg) : snap_exception("plugin", what_msg) {}
42 0 : explicit plugin_exception(QString const & what_msg) : snap_exception("plugin", what_msg) {}
43 : };
44 :
45 : class plugin_exception_invalid_order : public plugin_exception
46 : {
47 : public:
48 : explicit plugin_exception_invalid_order(char const * what_msg) : plugin_exception(what_msg) {}
49 : explicit plugin_exception_invalid_order(std::string const & what_msg) : plugin_exception(what_msg) {}
50 : explicit plugin_exception_invalid_order(QString const & what_msg) : plugin_exception(what_msg) {}
51 : };
52 :
53 :
54 : class plugin_signal
55 : {
56 : public:
57 : plugin_signal(const char * name);
58 : };
59 :
60 :
61 :
62 :
63 : class plugin
64 : {
65 : public:
66 : plugin();
67 : plugin(plugin const & rhs) = delete;
68 0 : virtual ~plugin() {}
69 :
70 : plugin & operator = (plugin const & rhs) = delete;
71 :
72 : void set_version(int version_major, int version_minor);
73 : void set_server_version(int version_major, int version_minor, int version_patch);
74 : int get_major_version() const;
75 : int get_minor_version() const;
76 : int get_server_major_version() const;
77 : int get_server_minor_version() const;
78 : int get_server_patch_version() const;
79 : QString get_plugin_name() const;
80 : int64_t last_modification() const;
81 : virtual QString icon() const;
82 : virtual QString description() const = 0;
83 : virtual QString plugin_categorization_tags() const;
84 : virtual QString help_uri() const;
85 : virtual QString settings_path() const;
86 : virtual QString dependencies() const = 0;
87 : virtual void bootstrap(snap_child * snap) = 0;
88 : virtual int64_t do_update(int64_t last_updated);
89 : virtual int64_t do_dynamic_update(int64_t last_updated);
90 :
91 : private:
92 : QString const f_name = QString();
93 : QString const f_filename = QString();
94 : mutable int64_t f_last_modification = 0;
95 : int32_t f_version_major = 0;
96 : int32_t f_version_minor = 0;
97 : int32_t f_server_version_major = 0;
98 : int32_t f_server_version_minor = 0;
99 : int32_t f_server_version_patch = 0;
100 : };
101 :
102 : typedef std::shared_ptr<plugin> plugin_ptr_t;
103 : typedef QMap<QString, plugin *> plugin_map_t;
104 : typedef QVector<plugin *> plugin_vector_t;
105 :
106 :
107 : class plugin_info
108 : {
109 : public:
110 : plugin_info(QString const & plugin_paths, QString const & name);
111 :
112 : QString const & get_name() const;
113 : QString const & get_filename() const;
114 : int64_t get_last_modification() const;
115 : QString const & get_icon() const;
116 : QString const & get_description() const;
117 : QString const & get_plugin_categorization_tags() const;
118 : QString const & get_help_uri() const;
119 : QString const & get_settings_path() const;
120 : QString const & get_dependencies() const;
121 : int32_t get_version_major() const;
122 : int32_t get_version_minor() const;
123 :
124 : private:
125 : QString f_name = QString();
126 : QString f_filename = QString();
127 : int64_t f_last_modification = 0;
128 : QString f_icon = QString();
129 : QString f_description = QString();
130 : QString f_categorization_tags = QString();
131 : QString f_help_uri = QString();
132 : QString f_settings_path = QString();
133 : QString f_dependencies = QString();
134 : int32_t f_version_major = 0;
135 : int32_t f_version_minor = 0;
136 : };
137 :
138 :
139 : snap_string_list list_all(QString const & plugin_path);
140 : bool load(QString const & plugin_path, snap_child * snap, plugin_ptr_t server, snap_string_list const & list_of_plugins, QString const & introducer);
141 : QString find_plugin_filename(snap_string_list const & plugin_paths, QString const & name);
142 : bool exists(QString const & name);
143 : void register_plugin(QString const & name, plugin * p);
144 : plugin * get_plugin(QString const & name);
145 : plugin_map_t const & get_plugin_list();
146 : plugin_vector_t const & get_plugin_vector();
147 : bool verify_plugin_name(QString const & name);
148 :
149 : /** \brief Initialize a plugin by creating a mini-factory.
150 : *
151 : * The factory is used to create a new instance of the plugin.
152 : *
153 : * Remember that we cannot have a plugin register all of its signals
154 : * in its constructor. This is because many of the other plugins
155 : * will otherwise not already be loaded and if missing at the time
156 : * we try to connect, the software breaks. This is why we have the
157 : * bootstrap() virtual function (very much like an init() function!)
158 : * and this macro automatically ensures that it gets called.
159 : *
160 : * The use of the macro is very simple, it is expected to appear
161 : * at the beginning in your filter implementation (.cpp) file:
162 : *
163 : * \code
164 : * SNAP_PLUGIN_START(plugin_name, 1, 0)
165 : * \endcode
166 : *
167 : * Replace \<plugin_name> with the name of your plugin. The following
168 : * numbers represent the major and minor version numbers of the plugin.
169 : * They are used to show the user what he's running with. The system
170 : * does not otherwise do much with that information at this point.
171 : *
172 : * The name of your plugin factory is:
173 : *
174 : * \code
175 : * plugin_\<name>_factory
176 : * \endcode
177 : *
178 : * And in order to get an instance, it defines a global variable named:
179 : *
180 : * \code
181 : * g_plugin_\<name>_factory
182 : * \endcode
183 : *
184 : * The factory has a few public functions you can call:
185 : * \li name *instance() -- It returns a pointer to your plugin (i.e. the
186 : * same as 'this' in your plugin functions.)
187 : * \li int version_major() const -- The major version number
188 : * \li int version_minor() const -- The minor version number
189 : * \li QString version() const -- The version as a string: "<major>.<minor>"
190 : *
191 : * The constructor is automatically called when dlopen() loads
192 : * your plugin and the destructor is automatically called whenever
193 : * dl unloads the plugins.
194 : *
195 : * At the end of your implementation you have to add the
196 : * SNAP_PLUGIN_END() macro to close the namespaces that the start
197 : * macro automatically adds.
198 : *
199 : * Note that the factory initialization also takes care of
200 : * initializing the Qt resources of the plugin (resources
201 : * are mandatory in all plugins because you are expected
202 : * to call the add_xml() function, although a few low
203 : * level plugins won't do that, they still need resources.)
204 : *
205 : * \note
206 : * If you create multiple .cpp files to implement your plugin
207 : * only one can use the SNAP_PLUGIN_START()/SNAP_PLUGIN_END()
208 : * macros. Otherwise you'd create multiple factories. In the
209 : * other files, make use of the SNAP_PLUGIN_EXTENSION_START()
210 : * and SNAP_PLUGIN_EXTENSION_END() macros instead.
211 : *
212 : * \todo
213 : * Look in a way to avoid the qInitResources_... if the plugin
214 : * has no resources.
215 : *
216 : * \param[in] name The name of the plugin.
217 : * \param[in] major The major version of the plugin.
218 : * \param[in] minor The minor version of the plugin.
219 : */
220 : #define SNAP_PLUGIN_START(name, major, minor) \
221 : extern int qInitResources_##name(); \
222 : namespace snap { namespace name { \
223 : class plugin_##name##_factory { \
224 : public: plugin_##name##_factory() : f_plugin(new name()) { \
225 : qInitResources_##name(); \
226 : f_plugin->set_version(major, minor); \
227 : f_plugin->set_server_version(SNAPWEBSITES_VERSION_MAJOR, SNAPWEBSITES_VERSION_MINOR, SNAPWEBSITES_VERSION_PATCH); \
228 : ::snap::plugins::register_plugin(#name, f_plugin); } \
229 : virtual ~plugin_##name##_factory() { delete f_plugin; } \
230 : plugin_##name##_factory(plugin_##name##_factory const &) = delete; \
231 : plugin_##name##_factory & operator = (plugin_##name##_factory const &) = delete; \
232 : name * instance() { return f_plugin; } \
233 : virtual int version_major() const { return major; } \
234 : virtual int version_minor() const { return minor; } \
235 : virtual QString version() const { return #major "." #minor; } \
236 : private: name * f_plugin; \
237 : } g_plugin_##name##_factory;
238 :
239 :
240 : /** \brief The counter part of the SNAP_PLUGIN_START() macro.
241 : *
242 : * This macro is used to close the namespaces opened by the
243 : * SNAP_PLUGIN_START() macro. It is used at the end of your
244 : * plugin implementation.
245 : */
246 : #define SNAP_PLUGIN_END() \
247 : }} // close namespaces
248 :
249 :
250 : /** \brief Initialize an extension (.cpp) file of a plugin.
251 : *
252 : * At time a plugin is really large. In order to write the plugin
253 : * in multiple .cpp files, you need to have a start and an end in
254 : * each file. Only one of the files can define the plugin factory
255 : * and use the SNAP_PLUGIN_START() macro. The other files want
256 : * to use this macro:
257 : *
258 : * \code
259 : * SNAP_PLUGIN_EXTENSION_START(plugin_name)
260 : * \endcode
261 : *
262 : * Replace \<plugin_name> with the name of your plugin. It has to
263 : * be the same in all the files.
264 : *
265 : * At the end of your implementation extension you have to add the
266 : * SNAP_PLUGIN_EXTENSION_END() macro to close the namespaces that the
267 : * start macro automatically adds.
268 : *
269 : * \param[in] name The name of the plugin.
270 : */
271 : #define SNAP_PLUGIN_EXTENSION_START(name) \
272 : namespace snap { namespace name {
273 :
274 :
275 : /** \brief End the extension (.cpp) file of a plugin.
276 : *
277 : * When you started a .cpp file with the SNAP_PLUGIN_EXTENSION_START()
278 : * then you want to end it with the SNAP_PLUGIN_EXTENSION_END() macro.
279 : *
280 : * \code
281 : * SNAP_PLUGIN_EXTENSION_END()
282 : * \endcode
283 : */
284 : #define SNAP_PLUGIN_EXTENSION_END() \
285 : }} // close namespaces
286 :
287 :
288 : /** \brief Conditionally listen to a signal.
289 : *
290 : * This function checks whether a given plugin was loaded and if so
291 : * listen to one of its signals.
292 : *
293 : * The macro accepts the name of the listener plugin (it must be
294 : * 'this'), the name of the emitter plugin, and the name of the
295 : * signal to listen to.
296 : *
297 : * The listener must have a function named on_\<name of signal>.
298 : * The emitter is expected to define the signal using the
299 : * SNAP_SIGNAL() macro so the signal is called
300 : * signal_listen_\<name of signal>.
301 : *
302 : * \param[in] name The name of the plugin connecting.
303 : * \param[in] emitter_name The name of the plugin emitting this signal.
304 : * \param[in] emitter_class The class with qualifiers if necessary of the plugin emitting this signal.
305 : * \param[in] signal The name of the signal to listen to.
306 : * \param[in] args The list of arguments to that signal.
307 : */
308 : #define SNAP_LISTEN(name, emitter_name, emitter_class, signal, args...) \
309 : if(::snap::plugins::exists(emitter_name)) \
310 : emitter_class::instance()->signal_listen_##signal( \
311 : boost::bind(&name::on_##signal, this, ##args));
312 :
313 : #define SNAP_LISTEN0(name, emitter_name, emitter_class, signal) \
314 : if(::snap::plugins::exists(emitter_name)) \
315 : emitter_class::instance()->signal_listen_##signal( \
316 : boost::bind(&name::on_##signal, this));
317 :
318 : /** \brief Compute the number of days in the month of February.
319 : * \private
320 : *
321 : * The month of February is used to adjust the date by 1 day over leap
322 : * years. Years are leap years when multiple of 4, but not if multiple
323 : * of 100, except if it is also a multiple of 400.
324 : *
325 : * The computation of a leap year is documented on Wikipedia:
326 : * http://www.wikipedia.org/wiki/Leap_year
327 : *
328 : * \param[in] year The year of the date to convert.
329 : *
330 : * \return 28 or 29 depending on whether the year is a leap year
331 : */
332 : #define _SNAP_UNIX_TIMESTAMP_FDAY(year) \
333 : (((year) % 400) == 0 ? 29LL : \
334 : (((year) % 100) == 0 ? 28LL : \
335 : (((year) % 4) == 0 ? 29LL : \
336 : 28LL)))
337 :
338 : /** \brief Compute the number of days in a year.
339 : * \private
340 : *
341 : * This macro returns the number of day from the beginning of the
342 : * year the (year, month, day) value represents.
343 : *
344 : * \param[in] year The 4 digits year concerned.
345 : * \param[in] month The month (1 to 12).
346 : * \param[in] day The day of the month (1 to 31)
347 : *
348 : * \return The number of days within that year (starting at 1)
349 : */
350 : #define _SNAP_UNIX_TIMESTAMP_YDAY(year, month, day) \
351 : ( \
352 : /* January */ static_cast<qint64>(day) \
353 : /* February */ + ((month) >= 2 ? 31LL : 0LL) \
354 : /* March */ + ((month) >= 3 ? _SNAP_UNIX_TIMESTAMP_FDAY(year) : 0LL) \
355 : /* April */ + ((month) >= 4 ? 31LL : 0LL) \
356 : /* May */ + ((month) >= 5 ? 30LL : 0LL) \
357 : /* June */ + ((month) >= 6 ? 31LL : 0LL) \
358 : /* July */ + ((month) >= 7 ? 30LL : 0LL) \
359 : /* August */ + ((month) >= 8 ? 31LL : 0LL) \
360 : /* September */+ ((month) >= 9 ? 31LL : 0LL) \
361 : /* October */ + ((month) >= 10 ? 30LL : 0LL) \
362 : /* November */ + ((month) >= 11 ? 31LL : 0LL) \
363 : /* December */ + ((month) >= 12 ? 30LL : 0LL) \
364 : )
365 :
366 : /** \brief Compute a Unix date from a hard coded date.
367 : *
368 : * This macro is used to compute a Unix date from a date defined as 6 numbers:
369 : * year, month, day, hour, minute, second. Each number is expected to be an
370 : * integer although it could very well be an expression. The computation takes
371 : * the year and month in account to compute the year day which is used by
372 : * the do_update() functions.
373 : *
374 : * The year is expected to be written as a 4 digit number (1998, 2012, etc.)
375 : *
376 : * Each number is expected to represent a valid date. If a number is out of
377 : * range, then the date is still computed. It will just represent a valid
378 : * date, just not exactly what you wrote down.
379 : *
380 : * The math used in this macro comes from a FreeBSD implementation of mktime
381 : * (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15)
382 : *
383 : * \code
384 : * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
385 : * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
386 : * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
387 : * \endcode
388 : *
389 : * Note that we expect the year to be 1970 and not 0, 2000 and not 30, etc.
390 : * For this reason our macro subtract values from the year that are different
391 : * from those shown in the FreeBSD sample code.
392 : *
393 : * Also the month must be a number from 1 to 12 and not 0 to 11 as used
394 : * in various Unix structures.
395 : *
396 : * \param[in] year The year representing this Unix timestamp.
397 : * \param[in] month The month representing this Unix timestamp.
398 : * \param[in] day The day representing this Unix timestamp.
399 : * \param[in] hour The hour representing this Unix timestamp.
400 : * \param[in] minute The minute representing this Unix timestamp.
401 : * \param[in] second The second representing this Unix timestamp.
402 : */
403 : #define SNAP_UNIX_TIMESTAMP(year, month, day, hour, minute, second) \
404 : ( /* time */ static_cast<qint64>(second) \
405 : + static_cast<qint64>(minute) * 60LL \
406 : + static_cast<qint64>(hour) * 3600LL \
407 : + /* year day (month + day) */ (_SNAP_UNIX_TIMESTAMP_YDAY(year, month, day) - 1) * 86400LL \
408 : + /* year */ (static_cast<qint64>(year) - 1970LL) * 31536000LL \
409 : + ((static_cast<qint64>(year) - 1969LL) / 4LL) * 86400LL \
410 : - ((static_cast<qint64>(year) - 1901LL) / 100LL) * 86400LL \
411 : + ((static_cast<qint64>(year) - 1601LL) / 400LL) * 86400LL )
412 :
413 : /** \brief Initialize the do_update() function.
414 : *
415 : * This macro is used to initialize the do_update() function by creating a
416 : * variable that is going to be given a date.
417 : */
418 : #define SNAP_PLUGIN_UPDATE_INIT() int64_t last_plugin_update(SNAP_UNIX_TIMESTAMP(1990, 1, 1, 0, 0, 0) * 1000000LL);
419 :
420 : /** \brief Create an update entry in your on_update() signal implementation.
421 : *
422 : * This macro is used to generate the necessary code to test the latest
423 : * update date and the date of the specified update.
424 : *
425 : * The function is called if the last time the website was updated it
426 : * was before this update. The function is then called with its own
427 : * date in micro-seconds (usec).
428 : *
429 : * \warning
430 : * The parameter to the on_update() function must be named last_updated for
431 : * this macro to compile as expected.
432 : *
433 : * \param[in] year The year representing this Unix timestamp.
434 : * \param[in] month The month representing this Unix timestamp.
435 : * \param[in] day The day representing this Unix timestamp.
436 : * \param[in] hour The hour representing this Unix timestamp.
437 : * \param[in] minute The minute representing this Unix timestamp.
438 : * \param[in] second The second representing this Unix timestamp.
439 : * \param[in] function The name of the function to call if the update is required.
440 : */
441 : #define SNAP_PLUGIN_UPDATE(year, month, day, hour, minute, second, function) \
442 : if(last_plugin_update > SNAP_UNIX_TIMESTAMP(year, month, day, hour, minute, second) * 1000000LL) { \
443 : throw plugins::plugin_exception_invalid_order("the updates in your do_update() functions must appear in increasing order in regard to date and time"); \
444 : } \
445 : last_plugin_update = SNAP_UNIX_TIMESTAMP(year, month, day, hour, minute, second) * 1000000LL; \
446 : if(last_updated < last_plugin_update) { \
447 : function(last_plugin_update); \
448 : }
449 :
450 : /** \brief End the plugin update function.
451 : *
452 : * Use the macro as the very last line in your plugin do_update() function.
453 : * It returns the lastest update of your plugin.
454 : *
455 : * This function adds a return statement so anything appearing after it
456 : * will be ignored (not reached.)
457 : */
458 : #define SNAP_PLUGIN_UPDATE_EXIT() return last_plugin_update;
459 :
460 :
461 : } // namespace plugins
462 : } // namespace snap
463 : // vim: ts=4 sw=4 et
|