1 /*
2     roxterm - VTE/GTK terminal emulator with tabs
3     Copyright (C) 2004-2015 Tony Houghton <h@realh.co.uk>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 
21 #include "dlg.h"
22 #include "globalopts.h"
23 #include "optsdbus.h"
24 
25 #ifdef ROXTERM_CAPPLET
26 #include "colourgui.h"
27 #include "configlet.h"
28 #include "profilegui.h"
29 #endif
30 
31 #include <string.h>
32 
33 #define DBUS_API_SUBJECT_TO_CHANGE
34 #include <dbus/dbus-glib-lowlevel.h>
35 
36 #define OPTSDBUS_NAME RTDBUS_NAME ".Options"
37 #define OPTSDBUS_OBJECT_PATH RTDBUS_OBJECT_PATH "/Options"
38 #define OPTSDBUS_INTERFACE RTDBUS_INTERFACE ".Options"
39 #define OPTSDBUS_ERROR RTDBUS_ERROR ".OptionsError"
40 
41 #define OPTSDBUS_STRING_SIGNAL "StringOption"
42 #define OPTSDBUS_INT_SIGNAL "IntOption"
43 #define OPTSDBUS_FLOAT_SIGNAL "FloatOption"
44 
45 #define OPTSDBUS_SET_PROFILE "SetProfile"
46 #define OPTSDBUS_SET_COLOUR_SCHEME "SetColourScheme"
47 #define OPTSDBUS_SET_SHORTCUT_SCHEME "SetShortcutScheme"
48 
49 #ifdef ROXTERM_CAPPLET
50 
optsdbus_read_args(DBusMessage * message,char const ** arg,DBusError * perror,DBusMessage ** preply_error)51 static gboolean optsdbus_read_args(DBusMessage *message, char const **arg,
52         DBusError *perror, DBusMessage **preply_error)
53 {
54     gboolean result = arg ? dbus_message_get_args(message, perror,
55                 DBUS_TYPE_STRING, arg,
56                 DBUS_TYPE_INVALID) :
57                 dbus_message_get_args(message, perror,
58                 DBUS_TYPE_INVALID);
59     if (!result)
60     {
61         rtdbus_whinge(perror, _("Unable to get argument from D-BUS message"));
62         if (!dbus_message_get_no_reply(message))
63         {
64             *preply_error = dbus_message_new_error(message, OPTSDBUS_ERROR,
65                     _("Unable to get argument"));
66         }
67         return FALSE;
68     }
69     return TRUE;
70 }
71 
optsdbus_method_handler(DBusConnection * connection,DBusMessage * message,void * user_data)72 static DBusHandlerResult optsdbus_method_handler(DBusConnection *connection,
73         DBusMessage *message, void *user_data)
74 {
75     DBusMessage *reply = NULL;
76     DBusError derror;
77     enum {
78         Unknown,
79         EditProfile,
80         EditColourScheme,
81         OpenConfiglet
82     } action = Unknown;
83     const char *profile_name = NULL;
84     char const **parg = NULL;
85     (void) connection;
86     (void) user_data;
87 
88     dbus_error_init(&derror);
89 
90     if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE, "EditProfile"))
91     {
92         action = EditProfile;
93     }
94     else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
95                 "EditColourScheme"))
96     {
97         action = EditColourScheme;
98     }
99     else if (dbus_message_is_method_call(message, OPTSDBUS_INTERFACE,
100                 "Configlet"))
101     {
102         action = OpenConfiglet;
103     }
104     if (action == EditProfile || action == EditColourScheme)
105         parg = &profile_name;
106     switch (action)
107     {
108         case EditProfile:
109         case EditColourScheme:
110         case OpenConfiglet:
111             if (!optsdbus_read_args(message, parg, &derror, &reply))
112             {
113                 break;
114             }
115             /* Fall-through */
116             switch (action)
117             {
118                 case EditProfile:
119                     profilegui_open(profile_name);
120                     break;
121                 case EditColourScheme:
122                     colourgui_open(profile_name);
123                     break;
124                 case OpenConfiglet:
125                     configlet_open();
126                     break;
127                 default:
128                     break;
129             }
130             if (!dbus_message_get_no_reply(message))
131                 reply = dbus_message_new_method_return(message);
132 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
133             if (profile_name)
134                 dbus_free(profile_name);
135 #endif
136             break;
137         default:
138             g_warning(_("Don't know how to handle method %s.%s"),
139                         dbus_message_get_interface(message),
140                         dbus_message_get_member(message));
141             break;
142     }
143 
144     if (reply)
145         rtdbus_send_message(reply);
146 
147     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
148 }
149 
optsdbus_signal_new(const char * signal_name,const char * profile_name,const char * key)150 static DBusMessage *optsdbus_signal_new(const char *signal_name,
151         const char *profile_name, const char *key)
152 {
153     return rtdbus_signal_new(OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
154             signal_name,
155             DBUS_TYPE_STRING, &profile_name,
156             DBUS_TYPE_STRING, &key,
157             DBUS_TYPE_INVALID);
158 }
159 
optsdbus_send_string_opt_signal(const char * profile_name,const char * key,const char * value)160 gboolean optsdbus_send_string_opt_signal(const char *profile_name,
161     const char *key, const char *value)
162 {
163     DBusMessage *message = optsdbus_signal_new(OPTSDBUS_STRING_SIGNAL,
164         profile_name, key);
165 
166     if (message)
167     {
168         DBusError derror;
169 
170         dbus_error_init(&derror);
171         if (!value)
172             value = "";
173         if (!rtdbus_append_args(message,
174                 DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID))
175         {
176             return FALSE;
177         }
178         else
179         {
180             return rtdbus_send_message(message);
181         }
182     }
183     return FALSE;
184 }
185 
optsdbus_send_int_opt_signal(const char * profile_name,const char * key,int value)186 gboolean optsdbus_send_int_opt_signal(const char *profile_name,
187     const char *key, int value)
188 {
189     DBusMessage *message;
190 
191     message = optsdbus_signal_new(OPTSDBUS_INT_SIGNAL,
192         profile_name, key);
193 
194     if (message)
195     {
196         DBusError derror;
197 
198         dbus_error_init(&derror);
199         if (!dbus_message_append_args(message,
200                 DBUS_TYPE_INT32, &value, DBUS_TYPE_INVALID))
201         {
202             return FALSE;
203         }
204         else
205         {
206             return rtdbus_send_message(message);
207         }
208     }
209     return FALSE;
210 }
211 
optsdbus_send_float_opt_signal(const char * profile_name,const char * key,double value)212 gboolean optsdbus_send_float_opt_signal(const char *profile_name,
213     const char *key, double value)
214 {
215     DBusMessage *message;
216 
217     message = optsdbus_signal_new(OPTSDBUS_FLOAT_SIGNAL,
218         profile_name, key);
219 
220     if (message)
221     {
222         DBusError derror;
223 
224         dbus_error_init(&derror);
225         if (!dbus_message_append_args(message,
226                 DBUS_TYPE_DOUBLE, &value, DBUS_TYPE_INVALID))
227         {
228             return FALSE;
229         }
230         else
231         {
232             return rtdbus_send_message(message);
233         }
234     }
235     return FALSE;
236 }
237 
optsdbus_send_stuff_changed_signal(const char * what_happened,const char * family_name,const char * old_name,const char * new_name)238 gboolean optsdbus_send_stuff_changed_signal(const char *what_happened,
239         const char *family_name, const char *old_name, const char *new_name)
240 {
241     /* We can cheat and use optsdbus_signal_new because it just happens
242      * to build a message with 2 string args; just what we want */
243     DBusMessage *message = optsdbus_signal_new(what_happened,
244         family_name, old_name);
245 
246     if (message)
247     {
248         DBusError derror;
249 
250         dbus_error_init(&derror);
251         if (new_name)
252         {
253             if (!dbus_message_append_args(message,
254                     DBUS_TYPE_STRING, &new_name, DBUS_TYPE_INVALID))
255             {
256                 return FALSE;
257             }
258         }
259         return rtdbus_send_message(message);
260     }
261     return FALSE;
262 }
263 
optsdbus_send_edit_opts_message(const char * method,const char * arg)264 gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg)
265 {
266     DBusMessage * message;
267 
268     message = rtdbus_method_new(OPTSDBUS_NAME,
269             OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE, method,
270             DBUS_TYPE_STRING, &arg,
271             DBUS_TYPE_INVALID);
272 
273     if (!message)
274         return FALSE;
275     return rtdbus_send_message(message);
276 }
277 
optsdbus_init(void)278 gboolean optsdbus_init(void)
279 {
280     return rtdbus_start_service(OPTSDBUS_NAME, OPTSDBUS_OBJECT_PATH,
281             optsdbus_method_handler, FALSE);
282 }
283 
284 #else /* !ROXTERM_CAPPLET */
285 
286 static OptsDBusOptionHandler optsdbus_option_handler = NULL;
287 static OptsDBusStuffChangedHandler optsdbus_stuff_changed_handler = NULL;
288 static OptsDBusSetProfileHandler optsdbus_set_profile_handler = NULL;
289 static OptsDBusSetProfileHandler optsdbus_set_colour_scheme_handler = NULL;
290 static OptsDBusSetProfileHandler optsdbus_set_shortcut_scheme_handler = NULL;
291 
optsdbus_set_profile_callback(OptsDBusSetProfileHandler handler,DBusMessage * message,DBusError * pderror)292 static gboolean optsdbus_set_profile_callback(OptsDBusSetProfileHandler handler,
293         DBusMessage *message, DBusError *pderror)
294 {
295     const char *id_str = NULL;
296     const char *profile_name = NULL;
297 
298     gboolean result = dbus_message_get_args(message, pderror,
299             DBUS_TYPE_STRING, &id_str,
300             DBUS_TYPE_STRING, &profile_name,
301             DBUS_TYPE_INVALID);
302     if (result)
303     {
304         void *id = NULL;
305 
306         if (sscanf(id_str, "%p", &id) == 1)
307         {
308             handler(id, profile_name);
309         }
310         else
311         {
312             dlg_warning(NULL,
313                     _("Unrecognised ROXTERM_ID '%s' in D-Bus message"), id_str);
314         }
315     }
316 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
317     if (profile_name)
318         dbus_free(profile_name);
319     if (id_str)
320         dbus_free(id_str);
321 #endif
322     return result;
323 }
324 
325 static DBusHandlerResult
optsdbus_message_filter(DBusConnection * connection,DBusMessage * message,void * user_data)326 optsdbus_message_filter(DBusConnection * connection, DBusMessage * message,
327     void *user_data)
328 {
329     const char *profile_name = NULL;
330     const char *key = NULL;
331     OptsDBusValue val;
332     char *dbus_str = NULL;
333     const char *signal_name;
334     gboolean args_result = TRUE;
335     DBusError derror;
336     OptsDBusOptType opt_type = OptsDBus_InvalidOpt;
337     const char *what_happened = NULL;
338     const char *family_name = NULL;
339     const char *current_name = NULL;
340     const char *new_name = NULL;
341 
342     (void) connection;
343     (void) user_data;
344 
345     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL ||
346         strcmp(dbus_message_get_path(message), OPTSDBUS_OBJECT_PATH))
347     {
348         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
349     }
350     signal_name = dbus_message_get_member(message);
351     if (strcmp(dbus_message_get_interface(message), OPTSDBUS_INTERFACE))
352     {
353         goto bad_signal;
354     }
355 
356     dbus_error_init(&derror);
357     if (!strcmp(signal_name, OPTSDBUS_STRING_SIGNAL))
358     {
359         args_result = dbus_message_get_args(message, &derror,
360             DBUS_TYPE_STRING, &profile_name,
361             DBUS_TYPE_STRING, &key,
362             DBUS_TYPE_STRING, &dbus_str, DBUS_TYPE_INVALID);
363         if (args_result)
364         {
365             opt_type = OptsDBus_StringOpt;
366             val.s = dbus_str && dbus_str[0] ? dbus_str : NULL;
367         }
368     }
369     else if (!strcmp(signal_name, OPTSDBUS_INT_SIGNAL))
370     {
371         args_result = dbus_message_get_args(message, &derror,
372             DBUS_TYPE_STRING, &profile_name,
373             DBUS_TYPE_STRING, &key,
374             DBUS_TYPE_INT32, &val.i, DBUS_TYPE_INVALID);
375         if (args_result)
376             opt_type = OptsDBus_IntOpt;
377     }
378     else if (!strcmp(signal_name, OPTSDBUS_FLOAT_SIGNAL))
379     {
380         args_result = dbus_message_get_args(message, &derror,
381             DBUS_TYPE_STRING, &profile_name,
382             DBUS_TYPE_STRING, &key,
383             DBUS_TYPE_DOUBLE, &val.f, DBUS_TYPE_INVALID);
384         if (args_result)
385             opt_type = OptsDBus_FloatOpt;
386     }
387     else if (!strcmp(signal_name, OPTSDBUS_DELETED)
388              || !strcmp(signal_name, OPTSDBUS_ADDED)
389              || !strcmp(signal_name, OPTSDBUS_RENAMED)
390              || !strcmp(signal_name, OPTSDBUS_CHANGED))
391     {
392         what_happened = signal_name;
393         if (strcmp(signal_name, OPTSDBUS_RENAMED))
394         {
395             args_result = dbus_message_get_args(message, &derror,
396                 DBUS_TYPE_STRING, &family_name,
397                 DBUS_TYPE_STRING, &current_name,
398                 DBUS_TYPE_INVALID);
399         }
400         else
401         {
402             args_result = dbus_message_get_args(message, &derror,
403                 DBUS_TYPE_STRING, &family_name,
404                 DBUS_TYPE_STRING, &current_name,
405                 DBUS_TYPE_STRING, &new_name,
406                 DBUS_TYPE_INVALID);
407         }
408     }
409     else if (!strcmp(signal_name, OPTSDBUS_SET_PROFILE))
410     {
411         if (optsdbus_set_profile_handler)
412         {
413             args_result = optsdbus_set_profile_callback(
414                     optsdbus_set_profile_handler, message, &derror);
415         }
416     }
417     else if (!strcmp(signal_name, OPTSDBUS_SET_COLOUR_SCHEME))
418     {
419         if (optsdbus_set_colour_scheme_handler)
420         {
421             args_result = optsdbus_set_profile_callback(
422                     optsdbus_set_colour_scheme_handler, message, &derror);
423         }
424     }
425     else if (!strcmp(signal_name, OPTSDBUS_SET_SHORTCUT_SCHEME))
426     {
427         if (optsdbus_set_shortcut_scheme_handler)
428         {
429             args_result = optsdbus_set_profile_callback(
430                     optsdbus_set_shortcut_scheme_handler, message, &derror);
431         }
432     }
433     else
434     {
435         goto bad_signal;
436     }
437     if (!args_result)
438     {
439         rtdbus_whinge(&derror, _("Unable to read D-BUS signal arguments"));
440     }
441     else
442     {
443         if (profile_name && optsdbus_option_handler)
444         {
445             (*optsdbus_option_handler) (profile_name, key, opt_type, val);
446         }
447         if (family_name && optsdbus_stuff_changed_handler)
448         {
449             (*optsdbus_stuff_changed_handler) (what_happened,
450                     family_name, current_name, new_name);
451         }
452     }
453 
454 #if ROXTERM_DBUS_OLD_ARGS_SEMANTICS
455     if (profile_name)
456         dbus_free(profile_name);
457     if (key)
458         dbus_free(key);
459     if (dbus_str)
460         dbus_free(dbus_str);
461     if (family_name)
462         dbus_free(family_name);
463     if (current_name)
464         dbus_free(current_name);
465     if (new_name)
466         dbus_free(new_name);
467 #endif
468     return DBUS_HANDLER_RESULT_HANDLED;
469 bad_signal:
470     g_warning(_("Unrecognised D-BUS signal %s.%s"),
471         dbus_message_get_interface(message), signal_name);
472     /* Consider it handled, because nothing else should be handling
473      * this object */
474     return DBUS_HANDLER_RESULT_HANDLED;
475 }
476 
optsdbus_enable_filter(void)477 static void optsdbus_enable_filter(void)
478 {
479     static gboolean enabled = FALSE;
480 
481     if (enabled)
482         return;
483     enabled = TRUE;
484     rtdbus_add_signal_rule_and_filter(
485             OPTSDBUS_OBJECT_PATH, OPTSDBUS_INTERFACE,
486             optsdbus_message_filter);
487 }
488 
optsdbus_listen_for_opt_signals(OptsDBusOptionHandler handler)489 void optsdbus_listen_for_opt_signals(OptsDBusOptionHandler handler)
490 {
491     g_return_if_fail(rtdbus_ok);
492     optsdbus_enable_filter();
493     optsdbus_option_handler = handler;
494 }
495 
optsdbus_listen_for_stuff_changed_signals(OptsDBusStuffChangedHandler handler)496 void optsdbus_listen_for_stuff_changed_signals(
497         OptsDBusStuffChangedHandler handler)
498 {
499     g_return_if_fail(rtdbus_ok);
500     optsdbus_enable_filter();
501     optsdbus_stuff_changed_handler = handler;
502 }
503 
optsdbus_listen_for_set_profile_signals(OptsDBusSetProfileHandler handler)504 void optsdbus_listen_for_set_profile_signals(OptsDBusSetProfileHandler handler)
505 {
506     g_return_if_fail(rtdbus_ok);
507     optsdbus_enable_filter();
508     optsdbus_set_profile_handler = handler;
509 }
510 
optsdbus_listen_for_set_colour_scheme_signals(OptsDBusSetProfileHandler handler)511 void optsdbus_listen_for_set_colour_scheme_signals(
512         OptsDBusSetProfileHandler handler)
513 {
514     g_return_if_fail(rtdbus_ok);
515     optsdbus_enable_filter();
516     optsdbus_set_colour_scheme_handler = handler;
517 }
518 
optsdbus_listen_for_set_shortcut_scheme_signals(OptsDBusSetProfileHandler handler)519 void optsdbus_listen_for_set_shortcut_scheme_signals(
520         OptsDBusSetProfileHandler handler)
521 {
522     g_return_if_fail(rtdbus_ok);
523     optsdbus_enable_filter();
524     optsdbus_set_shortcut_scheme_handler = handler;
525 }
526 
optsdbus_send_edit_opts_message(const char * method,const char * arg)527 gboolean optsdbus_send_edit_opts_message(const char *method, const char *arg)
528 {
529     GError *error = NULL;
530     char *appdir = global_options_appdir && global_options_appdir[0] ?
531         g_strdup_printf("'--appdir=%s'", global_options_appdir) : NULL;
532     char *bindir = global_options_bindir ?
533         g_strdup_printf("%s%c", global_options_bindir, G_DIR_SEPARATOR) :
534         NULL;
535     char *param = arg ? g_strdup_printf("--%s=%s", method, arg) :
536             g_strdup(method);
537     char *command =  g_strdup_printf("%sroxterm-config %s '%s'",
538                 bindir ? bindir : "", appdir ? appdir : "", param);
539 
540     g_free(bindir);
541     g_free(appdir);
542     g_free(param);
543     if (!g_spawn_command_line_async(command, &error))
544     {
545         dlg_critical(NULL, _("Error launching capplet: %s"), error->message);
546         g_error_free(error);
547         g_free(command);
548         return FALSE;
549     }
550     g_free(command);
551     return TRUE;
552 }
553 
554 #endif /* !ROXTERM_CAPPLET */
555 
556 /* vi:set sw=4 ts=4 noet cindent cino= */
557