1 /*
2  * plugins.c
3  * vim: expandtab:ts=4:sts=4:sw=4
4  *
5  * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
6  *
7  * This file is part of Profanity.
8  *
9  * Profanity is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Profanity is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
21  *
22  * In addition, as a special exception, the copyright holders give permission to
23  * link the code of portions of this program with the OpenSSL library under
24  * certain conditions as described in each individual source file, and
25  * distribute linked combinations including the two.
26  *
27  * You must obey the GNU General Public License in all respects for all of the
28  * code used other than OpenSSL. If you modify file(s) with this exception, you
29  * may extend this exception to your version of the file(s), but you are not
30  * obligated to do so. If you do not wish to do so, delete this exception
31  * statement from your version. If you delete this exception statement from all
32  * source files in the program, then also delete it here.
33  *
34  */
35 
36 #include <string.h>
37 #include <stdlib.h>
38 #include <gio/gio.h>
39 
40 #include "log.h"
41 #include "config.h"
42 #include "common.h"
43 #include "config/files.h"
44 #include "config/preferences.h"
45 #include "event/client_events.h"
46 #include "plugins/callbacks.h"
47 #include "plugins/autocompleters.h"
48 #include "plugins/api.h"
49 #include "plugins/plugins.h"
50 #include "plugins/themes.h"
51 #include "plugins/settings.h"
52 #include "plugins/disco.h"
53 #include "ui/ui.h"
54 #include "xmpp/xmpp.h"
55 
56 #ifdef HAVE_PYTHON
57 #include "plugins/python_plugins.h"
58 #include "plugins/python_api.h"
59 #endif
60 
61 #ifdef HAVE_C
62 #include "plugins/c_plugins.h"
63 #include "plugins/c_api.h"
64 #endif
65 
66 static GHashTable* plugins;
67 
68 void
plugins_init(void)69 plugins_init(void)
70 {
71     plugins = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
72     callbacks_init();
73     autocompleters_init();
74     plugin_themes_init();
75     plugin_settings_init();
76 
77 #ifdef HAVE_PYTHON
78     python_env_init();
79 #endif
80 #ifdef HAVE_C
81     c_env_init();
82 #endif
83 
84     // load plugins
85     gchar** plugins_pref = prefs_get_plugins();
86     if (plugins_pref) {
87         for (int i = 0; i < g_strv_length(plugins_pref); i++) {
88             gboolean loaded = FALSE;
89             gchar* filename = plugins_pref[i];
90 #ifdef HAVE_PYTHON
91             if (g_str_has_suffix(filename, ".py")) {
92                 ProfPlugin* plugin = python_plugin_create(filename);
93                 if (plugin) {
94                     g_hash_table_insert(plugins, strdup(filename), plugin);
95                     loaded = TRUE;
96                 }
97             }
98 #endif
99 #ifdef HAVE_C
100             if (g_str_has_suffix(filename, ".so")) {
101                 ProfPlugin* plugin = c_plugin_create(filename);
102                 if (plugin) {
103                     g_hash_table_insert(plugins, strdup(filename), plugin);
104                     loaded = TRUE;
105                 }
106             }
107 #endif
108             if (loaded) {
109                 log_info("Loaded plugin: %s", filename);
110             } else {
111                 log_info("Failed to load plugin: %s", filename);
112             }
113         }
114 
115         // initialise plugins
116         GList* values = g_hash_table_get_values(plugins);
117         GList* curr = values;
118         while (curr) {
119             ProfPlugin* plugin = curr->data;
120             plugin->init_func(plugin, PACKAGE_VERSION, PACKAGE_STATUS, NULL, NULL);
121             curr = g_list_next(curr);
122         }
123         g_list_free(values);
124     }
125 
126     prefs_free_plugins(plugins_pref);
127 
128     return;
129 }
130 
131 void
plugins_free_install_result(PluginsInstallResult * result)132 plugins_free_install_result(PluginsInstallResult* result)
133 {
134     if (!result) {
135         return;
136     }
137     g_slist_free_full(result->installed, free);
138     g_slist_free_full(result->failed, free);
139 }
140 
141 PluginsInstallResult*
plugins_install_all(const char * const path)142 plugins_install_all(const char* const path)
143 {
144     PluginsInstallResult* result = malloc(sizeof(PluginsInstallResult));
145     result->installed = NULL;
146     result->failed = NULL;
147     GSList* contents = NULL;
148     get_file_paths_recursive(path, &contents);
149 
150     GSList* curr = contents;
151     GString* error_message = NULL;
152     while (curr) {
153         error_message = g_string_new(NULL);
154         if (g_str_has_suffix(curr->data, ".py") || g_str_has_suffix(curr->data, ".so")) {
155             gchar* plugin_name = g_path_get_basename(curr->data);
156             if (plugins_install(plugin_name, curr->data, error_message)) {
157                 result->installed = g_slist_append(result->installed, strdup(curr->data));
158             } else {
159                 result->failed = g_slist_append(result->failed, strdup(curr->data));
160             }
161         }
162         curr = g_slist_next(curr);
163         g_string_free(error_message, TRUE);
164     }
165 
166     g_slist_free_full(contents, g_free);
167 
168     return result;
169 }
170 
171 gboolean
plugins_uninstall(const char * const plugin_name)172 plugins_uninstall(const char* const plugin_name)
173 {
174     plugins_unload(plugin_name);
175     char* plugins_dir = files_get_data_path(DIR_PLUGINS);
176     GString* target_path = g_string_new(plugins_dir);
177     free(plugins_dir);
178     g_string_append(target_path, "/");
179     g_string_append(target_path, plugin_name);
180     GFile* file = g_file_new_for_path(target_path->str);
181     GError* error = NULL;
182     gboolean result = g_file_delete(file, NULL, &error);
183     g_object_unref(file);
184     g_error_free(error);
185     g_string_free(target_path, TRUE);
186     return result;
187 }
188 
189 gboolean
plugins_install(const char * const plugin_name,const char * const filename,GString * error_message)190 plugins_install(const char* const plugin_name, const char* const filename, GString* error_message)
191 {
192     char* plugins_dir = files_get_data_path(DIR_PLUGINS);
193     GString* target_path = g_string_new(plugins_dir);
194     free(plugins_dir);
195     g_string_append(target_path, "/");
196     g_string_append(target_path, plugin_name);
197 
198     if (g_file_test(target_path->str, G_FILE_TEST_EXISTS)) {
199         log_info("Failed to install plugin: %s, file exists", plugin_name);
200         g_string_assign(error_message, "File exists");
201         return FALSE;
202     }
203 
204     gboolean result = copy_file(filename, target_path->str, false);
205     g_string_free(target_path, TRUE);
206 
207     if (result) {
208         result = plugins_load(plugin_name, error_message);
209     }
210     return result;
211 }
212 
213 GSList*
plugins_load_all(void)214 plugins_load_all(void)
215 {
216     GSList* plugins = plugins_unloaded_list();
217     GSList* loaded = NULL;
218     GSList* curr = plugins;
219     GString* error_message = NULL;
220     while (curr) {
221         error_message = g_string_new(NULL);
222         if (plugins_load(curr->data, error_message)) {
223             loaded = g_slist_append(loaded, strdup(curr->data));
224         }
225         curr = g_slist_next(curr);
226         g_string_free(error_message, TRUE);
227     }
228     g_slist_free_full(plugins, g_free);
229 
230     return loaded;
231 }
232 
233 gboolean
plugins_load(const char * const name,GString * error_message)234 plugins_load(const char* const name, GString* error_message)
235 {
236     ProfPlugin* plugin = g_hash_table_lookup(plugins, name);
237     if (plugin) {
238         log_info("Failed to load plugin: %s, plugin already loaded", name);
239         return FALSE;
240     }
241 
242     if (g_str_has_suffix(name, ".py")) {
243 #ifdef HAVE_PYTHON
244         plugin = python_plugin_create(name);
245 #else
246         g_string_assign(error_message, "Python plugins support is disabled.");
247 #endif
248     }
249 
250     if (g_str_has_suffix(name, ".so")) {
251 #ifdef HAVE_C
252         plugin = c_plugin_create(name);
253 #else
254         g_string_assign(error_message, "C plugins support is disabled.");
255 #endif
256     }
257     if (plugin) {
258         g_hash_table_insert(plugins, strdup(name), plugin);
259         if (connection_get_status() == JABBER_CONNECTED) {
260             const char* account_name = session_get_account_name();
261             const char* fulljid = connection_get_fulljid();
262             plugin->init_func(plugin, PACKAGE_VERSION, PACKAGE_STATUS, account_name, fulljid);
263         } else {
264             plugin->init_func(plugin, PACKAGE_VERSION, PACKAGE_STATUS, NULL, NULL);
265         }
266         log_info("Loaded plugin: %s", name);
267         prefs_add_plugin(name);
268         return TRUE;
269     } else {
270         log_info("Failed to load plugin: %s", name);
271         return FALSE;
272     }
273 }
274 
275 gboolean
plugins_unload_all(void)276 plugins_unload_all(void)
277 {
278     gboolean result = TRUE;
279     GList* plugin_names = g_hash_table_get_keys(plugins);
280     GList* plugin_names_dup = NULL;
281     GList* curr = plugin_names;
282     while (curr) {
283         plugin_names_dup = g_list_append(plugin_names_dup, strdup(curr->data));
284         curr = g_list_next(curr);
285     }
286     g_list_free(plugin_names);
287 
288     curr = plugin_names_dup;
289     while (curr) {
290         if (!plugins_unload(curr->data)) {
291             result = FALSE;
292         }
293         curr = g_list_next(curr);
294     }
295 
296     g_list_free_full(plugin_names_dup, free);
297 
298     return result;
299 }
300 
301 gboolean
plugins_unload(const char * const name)302 plugins_unload(const char* const name)
303 {
304     ProfPlugin* plugin = g_hash_table_lookup(plugins, name);
305     if (plugin) {
306         plugin->on_unload_func(plugin);
307 #ifdef HAVE_PYTHON
308         if (plugin->lang == LANG_PYTHON) {
309             python_plugin_destroy(plugin);
310         }
311 #endif
312 #ifdef HAVE_C
313         if (plugin->lang == LANG_C) {
314             c_plugin_destroy(plugin);
315         }
316 #endif
317         prefs_remove_plugin(name);
318         g_hash_table_remove(plugins, name);
319 
320         caps_reset_ver();
321         // resend presence to update server's disco info data for this client
322         if (connection_get_status() == JABBER_CONNECTED) {
323             char* account_name = session_get_account_name();
324             resource_presence_t last_presence = accounts_get_last_presence(account_name);
325             cl_ev_presence_send(last_presence, 0);
326         }
327         return TRUE;
328     }
329     return FALSE;
330 }
331 
332 void
plugins_reload_all(void)333 plugins_reload_all(void)
334 {
335     GList* plugin_names = g_hash_table_get_keys(plugins);
336     GList* plugin_names_dup = NULL;
337     GList* curr = plugin_names;
338     while (curr) {
339         plugin_names_dup = g_list_append(plugin_names_dup, strdup(curr->data));
340         curr = g_list_next(curr);
341     }
342     g_list_free(plugin_names);
343 
344     GString* error_message = NULL;
345     curr = plugin_names_dup;
346     while (curr) {
347         error_message = g_string_new(NULL);
348         plugins_reload(curr->data, error_message);
349         g_string_free(error_message, TRUE);
350         curr = g_list_next(curr);
351     }
352 
353     g_list_free_full(plugin_names_dup, free);
354 }
355 
356 gboolean
plugins_reload(const char * const name,GString * error_message)357 plugins_reload(const char* const name, GString* error_message)
358 {
359     gboolean res = plugins_unload(name);
360     if (res) {
361         res = plugins_load(name, error_message);
362     }
363 
364     return res;
365 }
366 
367 void
_plugins_unloaded_list_dir(const gchar * const dir,GSList ** result)368 _plugins_unloaded_list_dir(const gchar* const dir, GSList** result)
369 {
370     GDir* plugins_dir = g_dir_open(dir, 0, NULL);
371     if (plugins_dir == NULL) {
372         return;
373     }
374 
375     const gchar* plugin = g_dir_read_name(plugins_dir);
376     while (plugin) {
377         ProfPlugin* found = g_hash_table_lookup(plugins, plugin);
378         if ((g_str_has_suffix(plugin, ".so") || g_str_has_suffix(plugin, ".py")) && !found) {
379             *result = g_slist_append(*result, strdup(plugin));
380         }
381         plugin = g_dir_read_name(plugins_dir);
382     }
383     g_dir_close(plugins_dir);
384 }
385 
386 GSList*
plugins_unloaded_list(void)387 plugins_unloaded_list(void)
388 {
389     GSList* result = NULL;
390     char* plugins_dir = files_get_data_path(DIR_PLUGINS);
391     _plugins_unloaded_list_dir(plugins_dir, &result);
392     free(plugins_dir);
393 
394     return result;
395 }
396 
397 GList*
plugins_loaded_list(void)398 plugins_loaded_list(void)
399 {
400     return g_hash_table_get_keys(plugins);
401 }
402 
403 char*
plugins_autocomplete(const char * const input,gboolean previous)404 plugins_autocomplete(const char* const input, gboolean previous)
405 {
406     return autocompleters_complete(input, previous);
407 }
408 
409 void
plugins_reset_autocomplete(void)410 plugins_reset_autocomplete(void)
411 {
412     autocompleters_reset();
413 }
414 
415 void
plugins_win_process_line(char * win,const char * const line)416 plugins_win_process_line(char* win, const char* const line)
417 {
418     PluginWindowCallback* window = callbacks_get_window_handler(win);
419     if (window) {
420         window->callback_exec(window, win, line);
421     }
422 }
423 
424 void
plugins_close_win(const char * const plugin_name,const char * const tag)425 plugins_close_win(const char* const plugin_name, const char* const tag)
426 {
427     callbacks_remove_win(plugin_name, tag);
428 }
429 
430 void
plugins_on_start(void)431 plugins_on_start(void)
432 {
433     GList* values = g_hash_table_get_values(plugins);
434     GList* curr = values;
435     while (curr) {
436         ProfPlugin* plugin = curr->data;
437         plugin->on_start_func(plugin);
438         curr = g_list_next(curr);
439     }
440     g_list_free(values);
441 }
442 
443 void
plugins_on_shutdown(void)444 plugins_on_shutdown(void)
445 {
446     GList* values = g_hash_table_get_values(plugins);
447     GList* curr = values;
448     while (curr) {
449         ProfPlugin* plugin = curr->data;
450         plugin->on_shutdown_func(plugin);
451         curr = g_list_next(curr);
452     }
453     g_list_free(values);
454 }
455 
456 void
plugins_on_connect(const char * const account_name,const char * const fulljid)457 plugins_on_connect(const char* const account_name, const char* const fulljid)
458 {
459     GList* values = g_hash_table_get_values(plugins);
460     GList* curr = values;
461     while (curr) {
462         ProfPlugin* plugin = curr->data;
463         plugin->on_connect_func(plugin, account_name, fulljid);
464         curr = g_list_next(curr);
465     }
466     g_list_free(values);
467 }
468 
469 void
plugins_on_disconnect(const char * const account_name,const char * const fulljid)470 plugins_on_disconnect(const char* const account_name, const char* const fulljid)
471 {
472     GList* values = g_hash_table_get_values(plugins);
473     GList* curr = values;
474     while (curr) {
475         ProfPlugin* plugin = curr->data;
476         plugin->on_disconnect_func(plugin, account_name, fulljid);
477         curr = g_list_next(curr);
478     }
479     g_list_free(values);
480 }
481 
482 char*
plugins_pre_chat_message_display(const char * const barejid,const char * const resource,const char * message)483 plugins_pre_chat_message_display(const char* const barejid, const char* const resource, const char* message)
484 {
485     char* new_message = NULL;
486     char* curr_message = strdup(message);
487 
488     GList* values = g_hash_table_get_values(plugins);
489     GList* curr = values;
490     while (curr) {
491         ProfPlugin* plugin = curr->data;
492         new_message = plugin->pre_chat_message_display(plugin, barejid, resource, curr_message);
493         if (new_message) {
494             free(curr_message);
495             curr_message = strdup(new_message);
496             free(new_message);
497         }
498         curr = g_list_next(curr);
499     }
500     g_list_free(values);
501 
502     return curr_message;
503 }
504 
505 void
plugins_post_chat_message_display(const char * const barejid,const char * const resource,const char * message)506 plugins_post_chat_message_display(const char* const barejid, const char* const resource, const char* message)
507 {
508     GList* values = g_hash_table_get_values(plugins);
509     GList* curr = values;
510     while (curr) {
511         ProfPlugin* plugin = curr->data;
512         plugin->post_chat_message_display(plugin, barejid, resource, message);
513         curr = g_list_next(curr);
514     }
515     g_list_free(values);
516 }
517 
518 char*
plugins_pre_chat_message_send(const char * const barejid,const char * message)519 plugins_pre_chat_message_send(const char* const barejid, const char* message)
520 {
521     char* new_message = NULL;
522     char* curr_message = strdup(message);
523 
524     GList* values = g_hash_table_get_values(plugins);
525     GList* curr = values;
526     while (curr) {
527         ProfPlugin* plugin = curr->data;
528         if (plugin->contains_hook(plugin, "prof_pre_chat_message_send")) {
529             new_message = plugin->pre_chat_message_send(plugin, barejid, curr_message);
530             if (new_message) {
531                 free(curr_message);
532                 curr_message = strdup(new_message);
533                 free(new_message);
534             } else {
535                 free(curr_message);
536                 g_list_free(values);
537 
538                 return NULL;
539             }
540         }
541         curr = g_list_next(curr);
542     }
543     g_list_free(values);
544 
545     return curr_message;
546 }
547 
548 void
plugins_post_chat_message_send(const char * const barejid,const char * message)549 plugins_post_chat_message_send(const char* const barejid, const char* message)
550 {
551     GList* values = g_hash_table_get_values(plugins);
552     GList* curr = values;
553     while (curr) {
554         ProfPlugin* plugin = curr->data;
555         plugin->post_chat_message_send(plugin, barejid, message);
556         curr = g_list_next(curr);
557     }
558     g_list_free(values);
559 }
560 
561 char*
plugins_pre_room_message_display(const char * const barejid,const char * const nick,const char * message)562 plugins_pre_room_message_display(const char* const barejid, const char* const nick, const char* message)
563 {
564     char* new_message = NULL;
565     char* curr_message = strdup(message);
566 
567     GList* values = g_hash_table_get_values(plugins);
568     GList* curr = values;
569     while (curr) {
570         ProfPlugin* plugin = curr->data;
571         new_message = plugin->pre_room_message_display(plugin, barejid, nick, curr_message);
572         if (new_message) {
573             free(curr_message);
574             curr_message = strdup(new_message);
575             free(new_message);
576         }
577         curr = g_list_next(curr);
578     }
579     g_list_free(values);
580 
581     return curr_message;
582 }
583 
584 void
plugins_post_room_message_display(const char * const barejid,const char * const nick,const char * message)585 plugins_post_room_message_display(const char* const barejid, const char* const nick, const char* message)
586 {
587     GList* values = g_hash_table_get_values(plugins);
588     GList* curr = values;
589     while (curr) {
590         ProfPlugin* plugin = curr->data;
591         plugin->post_room_message_display(plugin, barejid, nick, message);
592         curr = g_list_next(curr);
593     }
594     g_list_free(values);
595 }
596 
597 char*
plugins_pre_room_message_send(const char * const barejid,const char * message)598 plugins_pre_room_message_send(const char* const barejid, const char* message)
599 {
600     char* new_message = NULL;
601     char* curr_message = strdup(message);
602 
603     GList* values = g_hash_table_get_values(plugins);
604     GList* curr = values;
605     while (curr) {
606         ProfPlugin* plugin = curr->data;
607         if (plugin->contains_hook(plugin, "prof_pre_room_message_send")) {
608             new_message = plugin->pre_room_message_send(plugin, barejid, curr_message);
609             if (new_message) {
610                 free(curr_message);
611                 curr_message = strdup(new_message);
612                 free(new_message);
613             } else {
614                 free(curr_message);
615                 g_list_free(values);
616 
617                 return NULL;
618             }
619         }
620         curr = g_list_next(curr);
621     }
622     g_list_free(values);
623 
624     return curr_message;
625 }
626 
627 void
plugins_post_room_message_send(const char * const barejid,const char * message)628 plugins_post_room_message_send(const char* const barejid, const char* message)
629 {
630     GList* values = g_hash_table_get_values(plugins);
631     GList* curr = values;
632     while (curr) {
633         ProfPlugin* plugin = curr->data;
634         plugin->post_room_message_send(plugin, barejid, message);
635         curr = g_list_next(curr);
636     }
637     g_list_free(values);
638 }
639 
640 void
plugins_on_room_history_message(const char * const barejid,const char * const nick,const char * const message,GDateTime * timestamp)641 plugins_on_room_history_message(const char* const barejid, const char* const nick, const char* const message,
642                                 GDateTime* timestamp)
643 {
644     char* timestamp_str = NULL;
645     GTimeVal timestamp_tv;
646     gboolean res = g_date_time_to_timeval(timestamp, &timestamp_tv);
647     if (res) {
648         timestamp_str = g_time_val_to_iso8601(&timestamp_tv);
649     }
650 
651     GList* values = g_hash_table_get_values(plugins);
652     GList* curr = values;
653     while (curr) {
654         ProfPlugin* plugin = curr->data;
655         plugin->on_room_history_message(plugin, barejid, nick, message, timestamp_str);
656         curr = g_list_next(curr);
657     }
658     g_list_free(values);
659 
660     free(timestamp_str);
661 }
662 
663 char*
plugins_pre_priv_message_display(const char * const fulljid,const char * message)664 plugins_pre_priv_message_display(const char* const fulljid, const char* message)
665 {
666     Jid* jidp = jid_create(fulljid);
667     char* new_message = NULL;
668     char* curr_message = strdup(message);
669 
670     GList* values = g_hash_table_get_values(plugins);
671     GList* curr = values;
672     while (curr) {
673         ProfPlugin* plugin = curr->data;
674         new_message = plugin->pre_priv_message_display(plugin, jidp->barejid, jidp->resourcepart, curr_message);
675         if (new_message) {
676             free(curr_message);
677             curr_message = strdup(new_message);
678             free(new_message);
679         }
680         curr = g_list_next(curr);
681     }
682     g_list_free(values);
683 
684     jid_destroy(jidp);
685     return curr_message;
686 }
687 
688 void
plugins_post_priv_message_display(const char * const fulljid,const char * message)689 plugins_post_priv_message_display(const char* const fulljid, const char* message)
690 {
691     Jid* jidp = jid_create(fulljid);
692 
693     GList* values = g_hash_table_get_values(plugins);
694     GList* curr = values;
695     while (curr) {
696         ProfPlugin* plugin = curr->data;
697         plugin->post_priv_message_display(plugin, jidp->barejid, jidp->resourcepart, message);
698         curr = g_list_next(curr);
699     }
700     g_list_free(values);
701 
702     jid_destroy(jidp);
703 }
704 
705 char*
plugins_pre_priv_message_send(const char * const fulljid,const char * const message)706 plugins_pre_priv_message_send(const char* const fulljid, const char* const message)
707 {
708     Jid* jidp = jid_create(fulljid);
709     char* new_message = NULL;
710     char* curr_message = strdup(message);
711 
712     GList* values = g_hash_table_get_values(plugins);
713     GList* curr = values;
714     while (curr) {
715         ProfPlugin* plugin = curr->data;
716         if (plugin->contains_hook(plugin, "prof_pre_priv_message_send")) {
717             new_message = plugin->pre_priv_message_send(plugin, jidp->barejid, jidp->resourcepart, curr_message);
718             if (new_message) {
719                 free(curr_message);
720                 curr_message = strdup(new_message);
721                 free(new_message);
722             } else {
723                 free(curr_message);
724                 g_list_free(values);
725                 jid_destroy(jidp);
726 
727                 return NULL;
728             }
729         }
730         curr = g_list_next(curr);
731     }
732     g_list_free(values);
733 
734     jid_destroy(jidp);
735     return curr_message;
736 }
737 
738 void
plugins_post_priv_message_send(const char * const fulljid,const char * const message)739 plugins_post_priv_message_send(const char* const fulljid, const char* const message)
740 {
741     Jid* jidp = jid_create(fulljid);
742 
743     GList* values = g_hash_table_get_values(plugins);
744     GList* curr = values;
745     while (curr) {
746         ProfPlugin* plugin = curr->data;
747         plugin->post_priv_message_send(plugin, jidp->barejid, jidp->resourcepart, message);
748         curr = g_list_next(curr);
749     }
750     g_list_free(values);
751 
752     jid_destroy(jidp);
753 }
754 
755 char*
plugins_on_message_stanza_send(const char * const text)756 plugins_on_message_stanza_send(const char* const text)
757 {
758     char* new_stanza = NULL;
759     char* curr_stanza = strdup(text);
760 
761     GList* values = g_hash_table_get_values(plugins);
762     GList* curr = values;
763     while (curr) {
764         ProfPlugin* plugin = curr->data;
765         new_stanza = plugin->on_message_stanza_send(plugin, curr_stanza);
766         if (new_stanza) {
767             free(curr_stanza);
768             curr_stanza = strdup(new_stanza);
769             free(new_stanza);
770         }
771         curr = g_list_next(curr);
772     }
773     g_list_free(values);
774 
775     return curr_stanza;
776 }
777 
778 gboolean
plugins_on_message_stanza_receive(const char * const text)779 plugins_on_message_stanza_receive(const char* const text)
780 {
781     gboolean cont = TRUE;
782 
783     GList* values = g_hash_table_get_values(plugins);
784     GList* curr = values;
785     while (curr) {
786         ProfPlugin* plugin = curr->data;
787         gboolean res = plugin->on_message_stanza_receive(plugin, text);
788         if (res == FALSE) {
789             cont = FALSE;
790         }
791         curr = g_list_next(curr);
792     }
793     g_list_free(values);
794 
795     return cont;
796 }
797 
798 char*
plugins_on_presence_stanza_send(const char * const text)799 plugins_on_presence_stanza_send(const char* const text)
800 {
801     char* new_stanza = NULL;
802     char* curr_stanza = strdup(text);
803 
804     GList* values = g_hash_table_get_values(plugins);
805     GList* curr = values;
806     while (curr) {
807         ProfPlugin* plugin = curr->data;
808         new_stanza = plugin->on_presence_stanza_send(plugin, curr_stanza);
809         if (new_stanza) {
810             free(curr_stanza);
811             curr_stanza = strdup(new_stanza);
812             free(new_stanza);
813         }
814         curr = g_list_next(curr);
815     }
816     g_list_free(values);
817 
818     return curr_stanza;
819 }
820 
821 gboolean
plugins_on_presence_stanza_receive(const char * const text)822 plugins_on_presence_stanza_receive(const char* const text)
823 {
824     gboolean cont = TRUE;
825 
826     GList* values = g_hash_table_get_values(plugins);
827     GList* curr = values;
828     while (curr) {
829         ProfPlugin* plugin = curr->data;
830         gboolean res = plugin->on_presence_stanza_receive(plugin, text);
831         if (res == FALSE) {
832             cont = FALSE;
833         }
834         curr = g_list_next(curr);
835     }
836     g_list_free(values);
837 
838     return cont;
839 }
840 
841 char*
plugins_on_iq_stanza_send(const char * const text)842 plugins_on_iq_stanza_send(const char* const text)
843 {
844     char* new_stanza = NULL;
845     char* curr_stanza = strdup(text);
846 
847     GList* values = g_hash_table_get_values(plugins);
848     GList* curr = values;
849     while (curr) {
850         ProfPlugin* plugin = curr->data;
851         new_stanza = plugin->on_iq_stanza_send(plugin, curr_stanza);
852         if (new_stanza) {
853             free(curr_stanza);
854             curr_stanza = strdup(new_stanza);
855             free(new_stanza);
856         }
857         curr = g_list_next(curr);
858     }
859     g_list_free(values);
860 
861     return curr_stanza;
862 }
863 
864 gboolean
plugins_on_iq_stanza_receive(const char * const text)865 plugins_on_iq_stanza_receive(const char* const text)
866 {
867     gboolean cont = TRUE;
868 
869     GList* values = g_hash_table_get_values(plugins);
870     GList* curr = values;
871     while (curr) {
872         ProfPlugin* plugin = curr->data;
873         gboolean res = plugin->on_iq_stanza_receive(plugin, text);
874         if (res == FALSE) {
875             cont = FALSE;
876         }
877         curr = g_list_next(curr);
878     }
879     g_list_free(values);
880 
881     return cont;
882 }
883 
884 void
plugins_on_contact_offline(const char * const barejid,const char * const resource,const char * const status)885 plugins_on_contact_offline(const char* const barejid, const char* const resource, const char* const status)
886 {
887     GList* values = g_hash_table_get_values(plugins);
888     GList* curr = values;
889     while (curr) {
890         ProfPlugin* plugin = curr->data;
891         plugin->on_contact_offline(plugin, barejid, resource, status);
892         curr = g_list_next(curr);
893     }
894     g_list_free(values);
895 }
896 
897 void
plugins_on_contact_presence(const char * const barejid,const char * const resource,const char * const presence,const char * const status,const int priority)898 plugins_on_contact_presence(const char* const barejid, const char* const resource, const char* const presence, const char* const status, const int priority)
899 {
900     GList* values = g_hash_table_get_values(plugins);
901     GList* curr = values;
902     while (curr) {
903         ProfPlugin* plugin = curr->data;
904         plugin->on_contact_presence(plugin, barejid, resource, presence, status, priority);
905         curr = g_list_next(curr);
906     }
907     g_list_free(values);
908 }
909 
910 void
plugins_on_chat_win_focus(const char * const barejid)911 plugins_on_chat_win_focus(const char* const barejid)
912 {
913     GList* values = g_hash_table_get_values(plugins);
914     GList* curr = values;
915     while (curr) {
916         ProfPlugin* plugin = curr->data;
917         plugin->on_chat_win_focus(plugin, barejid);
918         curr = g_list_next(curr);
919     }
920     g_list_free(values);
921 }
922 
923 void
plugins_on_room_win_focus(const char * const barejid)924 plugins_on_room_win_focus(const char* const barejid)
925 {
926     GList* values = g_hash_table_get_values(plugins);
927     GList* curr = values;
928     while (curr) {
929         ProfPlugin* plugin = curr->data;
930         plugin->on_room_win_focus(plugin, barejid);
931         curr = g_list_next(curr);
932     }
933     g_list_free(values);
934 }
935 
936 GList*
plugins_get_disco_features(void)937 plugins_get_disco_features(void)
938 {
939     return disco_get_features();
940 }
941 
942 void
plugins_shutdown(void)943 plugins_shutdown(void)
944 {
945     GList* values = g_hash_table_get_values(plugins);
946     GList* curr = values;
947 
948     while (curr) {
949 #ifdef HAVE_PYTHON
950         if (((ProfPlugin*)curr->data)->lang == LANG_PYTHON) {
951             python_plugin_destroy(curr->data);
952         }
953 #endif
954 #ifdef HAVE_C
955         if (((ProfPlugin*)curr->data)->lang == LANG_C) {
956             c_plugin_destroy(curr->data);
957         }
958 #endif
959 
960         curr = g_list_next(curr);
961     }
962     g_list_free(values);
963 #ifdef HAVE_PYTHON
964     python_shutdown();
965 #endif
966 #ifdef HAVE_C
967     c_shutdown();
968 #endif
969 
970     autocompleters_destroy();
971     plugin_themes_close();
972     plugin_settings_close();
973     callbacks_close();
974     disco_close();
975     g_hash_table_destroy(plugins);
976     plugins = NULL;
977 }
978