1 /*
2 * callbacks.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 "config.h"
37
38 #include <string.h>
39 #include <stdlib.h>
40
41 #include "command/cmd_defs.h"
42 #include "command/cmd_ac.h"
43 #include "plugins/callbacks.h"
44 #include "plugins/plugins.h"
45 #include "tools/autocomplete.h"
46 #include "tools/parser.h"
47 #include "ui/ui.h"
48 #include "ui/window_list.h"
49
50 static GHashTable* p_commands = NULL;
51 static GHashTable* p_timed_functions = NULL;
52 static GHashTable* p_window_callbacks = NULL;
53
54 static void
_free_window_callback(PluginWindowCallback * window_callback)55 _free_window_callback(PluginWindowCallback* window_callback)
56 {
57 if (window_callback->callback_destroy) {
58 window_callback->callback_destroy(window_callback->callback);
59 }
60 free(window_callback);
61 }
62
63 static void
_free_window_callbacks(GHashTable * window_callbacks)64 _free_window_callbacks(GHashTable* window_callbacks)
65 {
66 g_hash_table_destroy(window_callbacks);
67 }
68
69 static void
_free_command_help(CommandHelp * help)70 _free_command_help(CommandHelp* help)
71 {
72 int i = 0;
73 while (help->tags[i] != NULL) {
74 free(help->tags[i++]);
75 }
76
77 i = 0;
78 while (help->synopsis[i] != NULL) {
79 free(help->synopsis[i++]);
80 }
81
82 free(help->desc);
83
84 i = 0;
85 while (help->args[i] != NULL && help->args[i][0] != NULL) {
86 free(help->args[i][0]);
87 free(help->args[i][1]);
88 i++;
89 }
90
91 i = 0;
92 while (help->examples[i] != NULL) {
93 free(help->examples[i++]);
94 }
95
96 free(help);
97 }
98
99 static void
_free_command(PluginCommand * command)100 _free_command(PluginCommand* command)
101 {
102 if (command->callback_destroy) {
103 command->callback_destroy(command->callback);
104 }
105 free(command->command_name);
106
107 _free_command_help(command->help);
108
109 free(command);
110 }
111
112 static void
_free_command_hash(GHashTable * command_hash)113 _free_command_hash(GHashTable* command_hash)
114 {
115 g_hash_table_destroy(command_hash);
116 }
117
118 static void
_free_timed_function(PluginTimedFunction * timed_function)119 _free_timed_function(PluginTimedFunction* timed_function)
120 {
121 if (timed_function->callback_destroy) {
122 timed_function->callback_destroy(timed_function->callback);
123 }
124
125 g_timer_destroy(timed_function->timer);
126
127 free(timed_function);
128 }
129
130 static void
_free_timed_function_list(GList * timed_functions)131 _free_timed_function_list(GList* timed_functions)
132 {
133 g_list_free_full(timed_functions, (GDestroyNotify)_free_timed_function);
134 }
135
136 void
callbacks_init(void)137 callbacks_init(void)
138 {
139 p_commands = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_command_hash);
140 p_timed_functions = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_timed_function_list);
141 p_window_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_window_callbacks);
142 }
143
144 void
callbacks_remove(const char * const plugin_name)145 callbacks_remove(const char* const plugin_name)
146 {
147 GHashTable* command_hash = g_hash_table_lookup(p_commands, plugin_name);
148 if (command_hash) {
149 GList* commands = g_hash_table_get_keys(command_hash);
150 GList* curr = commands;
151 while (curr) {
152 char* command = curr->data;
153 cmd_ac_remove(command);
154 cmd_ac_remove_help(&command[1]);
155 curr = g_list_next(curr);
156 }
157 g_list_free(commands);
158 }
159
160 g_hash_table_remove(p_commands, plugin_name);
161 g_hash_table_remove(p_timed_functions, plugin_name);
162
163 GHashTable* tag_to_win_cb_hash = g_hash_table_lookup(p_window_callbacks, plugin_name);
164 if (tag_to_win_cb_hash) {
165 GList* tags = g_hash_table_get_keys(tag_to_win_cb_hash);
166 GList* curr = tags;
167 while (curr) {
168 wins_close_plugin(curr->data);
169 curr = g_list_next(curr);
170 }
171 g_list_free(tags);
172 }
173
174 g_hash_table_remove(p_window_callbacks, plugin_name);
175 }
176
177 void
callbacks_close(void)178 callbacks_close(void)
179 {
180 g_hash_table_destroy(p_commands);
181 g_hash_table_destroy(p_timed_functions);
182 g_hash_table_destroy(p_window_callbacks);
183 }
184
185 void
callbacks_add_command(const char * const plugin_name,PluginCommand * command)186 callbacks_add_command(const char* const plugin_name, PluginCommand* command)
187 {
188 GHashTable* command_hash = g_hash_table_lookup(p_commands, plugin_name);
189 if (command_hash) {
190 g_hash_table_insert(command_hash, strdup(command->command_name), command);
191 } else {
192 command_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_command);
193 g_hash_table_insert(command_hash, strdup(command->command_name), command);
194 g_hash_table_insert(p_commands, strdup(plugin_name), command_hash);
195 }
196 cmd_ac_add(command->command_name);
197 cmd_ac_add_help(&command->command_name[1]);
198 }
199
200 void
callbacks_add_timed(const char * const plugin_name,PluginTimedFunction * timed_function)201 callbacks_add_timed(const char* const plugin_name, PluginTimedFunction* timed_function)
202 {
203 GList* timed_function_list = g_hash_table_lookup(p_timed_functions, plugin_name);
204 if (timed_function_list) {
205 // we assign this so we dont get: -Werror=unused-result
206 timed_function_list = g_list_append(timed_function_list, timed_function);
207 } else {
208 timed_function_list = g_list_append(timed_function_list, timed_function);
209 g_hash_table_insert(p_timed_functions, strdup(plugin_name), timed_function_list);
210 }
211 }
212
213 gboolean
callbacks_win_exists(const char * const plugin_name,const char * tag)214 callbacks_win_exists(const char* const plugin_name, const char* tag)
215 {
216 GHashTable* window_callbacks = g_hash_table_lookup(p_window_callbacks, plugin_name);
217 if (window_callbacks) {
218 PluginWindowCallback* cb = g_hash_table_lookup(window_callbacks, tag);
219 if (cb) {
220 return TRUE;
221 }
222 }
223
224 return FALSE;
225 }
226
227 void
callbacks_remove_win(const char * const plugin_name,const char * const tag)228 callbacks_remove_win(const char* const plugin_name, const char* const tag)
229 {
230 GHashTable* window_callbacks = g_hash_table_lookup(p_window_callbacks, plugin_name);
231 if (window_callbacks) {
232 g_hash_table_remove(window_callbacks, tag);
233 }
234 }
235
236 void
callbacks_add_window_handler(const char * const plugin_name,const char * tag,PluginWindowCallback * window_callback)237 callbacks_add_window_handler(const char* const plugin_name, const char* tag, PluginWindowCallback* window_callback)
238 {
239 GHashTable* window_callbacks = g_hash_table_lookup(p_window_callbacks, plugin_name);
240 if (window_callbacks) {
241 g_hash_table_insert(window_callbacks, strdup(tag), window_callback);
242 } else {
243 window_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_window_callback);
244 g_hash_table_insert(window_callbacks, strdup(tag), window_callback);
245 g_hash_table_insert(p_window_callbacks, strdup(plugin_name), window_callbacks);
246 }
247 }
248
249 void*
callbacks_get_window_handler(const char * tag)250 callbacks_get_window_handler(const char* tag)
251 {
252 if (p_window_callbacks) {
253 GList* window_callback_hashes = g_hash_table_get_values(p_window_callbacks);
254 GList* curr_hash = window_callback_hashes;
255 while (curr_hash) {
256 GHashTable* window_callback_hash = curr_hash->data;
257 PluginWindowCallback* callback = g_hash_table_lookup(window_callback_hash, tag);
258 if (callback) {
259 g_list_free(window_callback_hashes);
260 return callback;
261 }
262
263 curr_hash = g_list_next(curr_hash);
264 }
265
266 g_list_free(window_callback_hashes);
267 return NULL;
268 } else {
269 return NULL;
270 }
271 }
272
273 gboolean
plugins_run_command(const char * const input)274 plugins_run_command(const char* const input)
275 {
276 gchar** split = g_strsplit(input, " ", -1);
277
278 GList* command_hashes = g_hash_table_get_values(p_commands);
279 GList* curr_hash = command_hashes;
280 while (curr_hash) {
281 GHashTable* command_hash = curr_hash->data;
282
283 PluginCommand* command = g_hash_table_lookup(command_hash, split[0]);
284 if (command) {
285 gboolean result;
286 gchar** args = parse_args_with_freetext(input, command->min_args, command->max_args, &result);
287 if (result == FALSE) {
288 ui_invalid_command_usage(command->command_name, NULL);
289 g_strfreev(split);
290 g_list_free(command_hashes);
291 return TRUE;
292 } else {
293 command->callback_exec(command, args);
294 g_strfreev(split);
295 g_strfreev(args);
296 g_list_free(command_hashes);
297 return TRUE;
298 }
299 }
300
301 curr_hash = g_list_next(curr_hash);
302 }
303
304 g_list_free(command_hashes);
305 g_strfreev(split);
306 return FALSE;
307 }
308
309 CommandHelp*
plugins_get_help(const char * const cmd)310 plugins_get_help(const char* const cmd)
311 {
312 GList* command_hashes = g_hash_table_get_values(p_commands);
313 GList* curr_hash = command_hashes;
314 while (curr_hash) {
315 GHashTable* command_hash = curr_hash->data;
316
317 PluginCommand* command = g_hash_table_lookup(command_hash, cmd);
318 if (command) {
319 g_list_free(command_hashes);
320 return command->help;
321 }
322
323 curr_hash = g_list_next(curr_hash);
324 }
325
326 g_list_free(command_hashes);
327
328 return NULL;
329 }
330
331 void
plugins_run_timed(void)332 plugins_run_timed(void)
333 {
334 GList* timed_functions_lists = g_hash_table_get_values(p_timed_functions);
335
336 GList* curr_list = timed_functions_lists;
337 while (curr_list) {
338 GList* timed_function_list = curr_list->data;
339 GList* curr = timed_function_list;
340 while (curr) {
341 PluginTimedFunction* timed_function = curr->data;
342
343 gdouble elapsed = g_timer_elapsed(timed_function->timer, NULL);
344
345 if (timed_function->interval_seconds > 0 && elapsed >= timed_function->interval_seconds) {
346 timed_function->callback_exec(timed_function);
347 g_timer_start(timed_function->timer);
348 }
349
350 curr = g_list_next(curr);
351 }
352 curr_list = g_list_next(curr_list);
353 }
354
355 g_list_free(timed_functions_lists);
356 }
357
358 GList*
plugins_get_command_names(void)359 plugins_get_command_names(void)
360 {
361 GList* result = NULL;
362
363 GList* command_hashes = g_hash_table_get_values(p_commands);
364 GList* curr_hash = command_hashes;
365 while (curr_hash) {
366 GHashTable* command_hash = curr_hash->data;
367 GList* commands = g_hash_table_get_keys(command_hash);
368 GList* curr = commands;
369 while (curr) {
370 char* command = curr->data;
371 result = g_list_append(result, command);
372 curr = g_list_next(curr);
373 }
374 g_list_free(commands);
375 curr_hash = g_list_next(curr_hash);
376 }
377
378 g_list_free(command_hashes);
379
380 return result;
381 }
382