1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* ibus - The Input Bus
4  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
6  * Copyright (C) 2008-2018 Red Hat, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 
24 #include "ibusshare.h"
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <glib-object.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ibus.h>
35 
36 static gchar *_display = NULL;
37 
38 const gchar *
ibus_get_local_machine_id(void)39 ibus_get_local_machine_id (void)
40 {
41     static gchar *machine_id = NULL;
42 
43     if (machine_id == NULL) {
44         GError *error = NULL;
45         if (!g_file_get_contents ("/var/lib/dbus/machine-id",
46                                   &machine_id,
47                                   NULL,
48                                   &error) &&
49             !g_file_get_contents ("/etc/machine-id",
50                                   &machine_id,
51                                   NULL,
52                                   NULL)) {
53             g_warning ("Unable to load /var/lib/dbus/machine-id: %s", error->message);
54             machine_id = "machine-id";
55         }
56         else {
57             g_strstrip (machine_id);
58         }
59         if (error != NULL) {
60             g_error_free (error);
61         }
62     }
63 
64     return machine_id;
65 }
66 
67 void
ibus_set_display(const gchar * display)68 ibus_set_display (const gchar *display)
69 {
70     if (_display != NULL)
71         g_free (_display);
72     _display = g_strdup (display);
73 }
74 
75 const gchar *
ibus_get_user_name(void)76 ibus_get_user_name (void)
77 {
78     return g_get_user_name ();
79 }
80 
81 glong
ibus_get_daemon_uid(void)82 ibus_get_daemon_uid (void)
83 {
84     return getuid ();
85 }
86 
87 const gchar *
ibus_get_session_id(void)88 ibus_get_session_id (void)
89 {
90     return g_getenv("IBUS_SESSION_ID");
91 }
92 
93 const gchar *
ibus_get_socket_path(void)94 ibus_get_socket_path (void)
95 {
96     static gchar *path = NULL;
97 
98     if (path == NULL) {
99         gchar *hostname = "unix";
100         gchar *display;
101         gchar *displaynumber = "0";
102         /* gchar *screennumber = "0"; */
103         gboolean is_wayland = FALSE;
104         gchar *p;
105 
106         path = g_strdup (g_getenv ("IBUS_ADDRESS_FILE"));
107         if (path != NULL) {
108             return path;
109         }
110 
111         if (_display == NULL) {
112             display = g_strdup (g_getenv ("WAYLAND_DISPLAY"));
113             if (display)
114                 is_wayland = TRUE;
115             else
116                 display = g_strdup (g_getenv ("DISPLAY"));
117         }
118         else {
119             display = g_strdup (_display);
120         }
121 
122         if (is_wayland) {
123             displaynumber = display;
124         } else if (display) {
125             p = display;
126             hostname = display;
127             for (; *p != ':' && *p != '\0'; p++);
128 
129             if (*p == ':') {
130                 *p = '\0';
131                 p++;
132                 displaynumber = p;
133             }
134 
135             for (; *p != '.' && *p != '\0'; p++);
136 
137             if (*p == '.') {
138                 *p = '\0';
139                 p++;
140                 /* Do not use screennumber
141                  screennumber = p; */
142             }
143         }
144 
145         if (hostname[0] == '\0')
146             hostname = "unix";
147 
148         p = g_strdup_printf ("%s-%s-%s",
149                              ibus_get_local_machine_id (),
150                              hostname,
151                              displaynumber);
152         /* Qt5 IBus module has a hard-coded path and we cannot change this
153          * for the back compatibility.
154          * XDG_RUNTIME_DIR is not useful because it's generated by
155          * login but not `su` command and ibus-daemon can be run with `su`
156          * and we may change the path to XDG_CACHE_HOME in the future.
157          */
158         path = g_build_filename (g_get_user_config_dir (),
159                                  "ibus",
160                                  "bus",
161                                  p,
162                                  NULL);
163         g_free (p);
164         g_free (display);
165     }
166     return path;
167 }
168 
169 gint
ibus_get_timeout(void)170 ibus_get_timeout (void)
171 {
172     /* 16000 ms is the default timeout on the ibus-daemon side
173      * (15 sec) plus 1. */
174     static const gint default_timeout = 16000;
175 
176     static gint64 timeout = -2;
177     if (timeout == -2) {
178         const gchar *timeout_str = g_getenv ("IBUS_TIMEOUT");
179         if (timeout_str == NULL) {
180             timeout = default_timeout;
181         } else {
182             timeout = g_ascii_strtoll(timeout_str, NULL, 10);
183             if (timeout < -1 || timeout == 0 || timeout > G_MAXINT) {
184                 timeout = default_timeout;
185             }
186         }
187     }
188     return timeout;
189 }
190 
191 const gchar *
ibus_get_address(void)192 ibus_get_address (void)
193 {
194     static gchar *address = NULL;
195     pid_t pid = -1;
196     static gchar buffer[1024];
197     FILE *pf;
198 
199     /* free address */
200     if (address != NULL) {
201         g_free (address);
202         address = NULL;
203     }
204 
205     /* get address from env variable */
206     address = g_strdup (g_getenv ("IBUS_ADDRESS"));
207     if (address) {
208         return address;
209     }
210 
211     /* read address from ~/.config/ibus/bus/soketfile */
212     pf = fopen (ibus_get_socket_path (), "r");
213     if (pf == NULL) {
214         return NULL;
215     }
216 
217     while (!feof (pf)) {
218         gchar *p = buffer;
219         if (fgets (buffer, sizeof (buffer), pf) == NULL)
220             break;
221 
222         /* skip comment line */
223         if (p[0] == '#')
224             continue;
225         /* parse IBUS_ADDRESS */
226         if (strncmp (p, "IBUS_ADDRESS=", sizeof ("IBUS_ADDRESS=") - 1) == 0) {
227             address = p + sizeof ("IBUS_ADDRESS=") - 1;
228             for (p = (gchar *)address; *p != '\n' && *p != '\0'; p++);
229             if (*p == '\n')
230                 *p = '\0';
231             address = g_strdup (address);
232             continue;
233         }
234 
235         /* parse IBUS_DAEMON_PID */
236         if (strncmp (p, "IBUS_DAEMON_PID=", sizeof ("IBUS_DAEMON_PID=") - 1) == 0) {
237             pid = atoi(p + sizeof ("IBUS_DAEMON_PID=") - 1);
238             continue;
239         }
240 
241     }
242     fclose (pf);
243 
244     if (pid == -1 || kill (pid, 0) != 0) {
245         return NULL;
246     }
247 
248     return address;
249 }
250 
251 void
ibus_write_address(const gchar * address)252 ibus_write_address (const gchar *address)
253 {
254     FILE *pf;
255     gchar *path;
256     g_return_if_fail (address != NULL);
257 
258     path = g_path_get_dirname (ibus_get_socket_path ());
259     g_mkdir_with_parents (path, 0700);
260     g_free (path);
261 
262     g_unlink (ibus_get_socket_path ());
263     pf = fopen (ibus_get_socket_path (), "w");
264     g_return_if_fail (pf != NULL);
265 
266     fprintf (pf,
267         "# This file is created by ibus-daemon, please do not modify it.\n"
268         "# This file allows processes on the machine to find the\n"
269         "# ibus session bus with the below address.\n"
270         "# If the IBUS_ADDRESS environment variable is set, it will\n"
271         "# be used rather than this file.\n"
272         "IBUS_ADDRESS=%s\n"
273         "IBUS_DAEMON_PID=%ld\n",
274         address, (glong) getpid ());
275     fclose (pf);
276 }
277 
278 void
ibus_free_strv(gchar ** strv)279 ibus_free_strv (gchar **strv)
280 {
281     gchar **p;
282 
283     if (strv == NULL)
284         return;
285 
286     for (p = strv; *p != NULL; p++) {
287         g_free (*p);
288     }
289 
290     g_free (strv);
291 }
292 
293 void
ibus_init(void)294 ibus_init (void)
295 {
296 #if !GLIB_CHECK_VERSION(2,35,0)
297     g_type_init ();
298 #endif
299     IBUS_ERROR;
300     IBUS_TYPE_TEXT;
301     IBUS_TYPE_ATTRIBUTE;
302     IBUS_TYPE_ATTR_LIST;
303     IBUS_TYPE_LOOKUP_TABLE;
304     IBUS_TYPE_COMPONENT;
305     IBUS_TYPE_ENGINE_DESC;
306     IBUS_TYPE_OBSERVED_PATH;
307     IBUS_TYPE_REGISTRY;
308     IBUS_TYPE_X_EVENT;
309 }
310 
311 static GMainLoop *main_loop = NULL;
312 
313 void
ibus_main(void)314 ibus_main (void)
315 {
316     main_loop = g_main_loop_new (NULL, FALSE);
317 
318     g_main_loop_run (main_loop);
319 
320     g_main_loop_unref (main_loop);
321     main_loop = NULL;
322 }
323 
324 void
ibus_quit(void)325 ibus_quit (void)
326 {
327     if (main_loop) {
328         g_main_loop_quit (main_loop);
329     }
330 }
331 
332 static gboolean ibus_log_handler_is_verbose = FALSE;
333 static guint ibus_log_handler_id = 0;
334 
335 static void
ibus_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)336 ibus_log_handler (const gchar    *log_domain,
337                   GLogLevelFlags  log_level,
338                   const gchar    *message,
339                   gpointer        user_data)
340 {
341     // In the quiet mode (i.e. not verbose), we'll ignore DEBUG and
342     // WARNING messages.
343     if (!ibus_log_handler_is_verbose &&
344         ((log_level & G_LOG_LEVEL_DEBUG) ||
345          (log_level & G_LOG_LEVEL_WARNING))) {
346         return;
347     }
348     // Add timing info like "17:34:57.680038" (hour, min, sec, microsecond).
349     struct timeval time_val;
350     gettimeofday (&time_val, NULL);
351     struct tm local_time;
352     localtime_r (&time_val.tv_sec, &local_time);
353     char* new_message =
354         g_strdup_printf ("%02d:%02d:%02d.%6d: %s",
355                          local_time.tm_hour,
356                          local_time.tm_min,
357                          local_time.tm_sec,
358                          (int)time_val.tv_usec,
359                          message);
360     g_log_default_handler (log_domain, log_level, new_message, user_data);
361     g_free (new_message);
362 }
363 
364 void
ibus_set_log_handler(gboolean verbose)365 ibus_set_log_handler (gboolean verbose)
366 {
367     if (ibus_log_handler_id != 0) {
368         g_log_remove_handler (G_LOG_DOMAIN, ibus_log_handler_id);
369     }
370 
371     ibus_log_handler_is_verbose = verbose;
372     ibus_log_handler_id = g_log_set_handler (G_LOG_DOMAIN,
373                                              G_LOG_LEVEL_MASK,
374                                              ibus_log_handler,
375                                              NULL);
376 }
377 
378 void
ibus_unset_log_handler(void)379 ibus_unset_log_handler (void)
380 {
381     if (ibus_log_handler_id != 0) {
382         g_log_remove_handler (G_LOG_DOMAIN, ibus_log_handler_id);
383         ibus_log_handler_id = 0;
384     }
385 }
386