1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3  * Copyright (C) 2011 Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * 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., 51 Franklin Street - Suite 500, Boston, MA
18  * 02110-1335, USA.
19  *
20  * Authors: David Zeuthen <davidz@redhat.com>
21  *          Cosimo Cecchi <cosimoc@redhat.com>
22  *
23  */
24 
25 #include "cinnamon-mime-sniffer.h"
26 #include "hotplug-mimetypes.h"
27 
28 /* Set the environment variable HOTPLUG_SNIFFER_DEBUG to show debug */
29 static void print_debug (const gchar *str, ...);
30 
31 #define BUS_NAME "org.Cinnamon.HotplugSniffer"
32 #define AUTOQUIT_TIMEOUT 5
33 
34 static const gchar introspection_xml[] =
35   "<node>"
36   "  <interface name='org.Cinnamon.HotplugSniffer'>"
37   "    <method name='SniffURI'>"
38   "      <arg type='s' name='uri' direction='in'/>"
39   "      <arg type='as' name='content_types' direction='out'/>"
40   "    </method>"
41   "  </interface>"
42   "</node>";
43 
44 static GDBusNodeInfo *introspection_data = NULL;
45 static GMainLoop     *loop = NULL;
46 static guint          autoquit_id = 0;
47 
48 static gboolean
autoquit_timeout_cb(gpointer _unused)49 autoquit_timeout_cb (gpointer _unused)
50 {
51   print_debug ("Timeout reached, quitting...");
52 
53   autoquit_id = 0;
54   g_main_loop_quit (loop);
55 
56   return FALSE;
57 }
58 
59 static void
ensure_autoquit_off(void)60 ensure_autoquit_off (void)
61 {
62   if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
63     return;
64 
65   if (autoquit_id != 0)
66     {
67       g_source_remove (autoquit_id);
68       autoquit_id = 0;
69     }
70 }
71 
72 static void
ensure_autoquit_on(void)73 ensure_autoquit_on (void)
74 {
75   if (g_getenv ("HOTPLUG_SNIFFER_PERSIST") != NULL)
76     return;
77 
78   autoquit_id =
79     g_timeout_add_seconds (AUTOQUIT_TIMEOUT,
80                            autoquit_timeout_cb, NULL);
81 }
82 
83 typedef struct {
84   GVariant *parameters;
85   GDBusMethodInvocation *invocation;
86 } InvocationData;
87 
88 static InvocationData *
invocation_data_new(GVariant * params,GDBusMethodInvocation * invocation)89 invocation_data_new (GVariant *params,
90                      GDBusMethodInvocation *invocation)
91 {
92   InvocationData *ret;
93 
94   ret = g_slice_new0 (InvocationData);
95   ret->parameters = g_variant_ref (params);
96   ret->invocation = g_object_ref (invocation);
97 
98   return ret;
99 }
100 
101 static void
invocation_data_free(InvocationData * data)102 invocation_data_free (InvocationData *data)
103 {
104   g_variant_unref (data->parameters);
105   g_clear_object (&data->invocation);
106 
107   g_slice_free (InvocationData, data);
108 }
109 
110 static void
sniff_async_ready_cb(GObject * source,GAsyncResult * res,gpointer user_data)111 sniff_async_ready_cb (GObject *source,
112                       GAsyncResult *res,
113                       gpointer user_data)
114 {
115   InvocationData *data = user_data;
116   gchar **types;
117   GError *error = NULL;
118 
119   types = cinnamon_mime_sniffer_sniff_finish (CINNAMON_MIME_SNIFFER (source),
120                                            res, &error);
121 
122   if (error != NULL)
123     {
124       g_dbus_method_invocation_return_gerror (data->invocation, error);
125       g_error_free (error);
126       goto out;
127     }
128 
129   g_dbus_method_invocation_return_value (data->invocation,
130                                          g_variant_new ("(^as)", types));
131   g_strfreev (types);
132 
133  out:
134   invocation_data_free (data);
135   ensure_autoquit_on ();
136 }
137 
138 static void
handle_sniff_uri(InvocationData * data)139 handle_sniff_uri (InvocationData *data)
140 {
141   CinnamonMimeSniffer *sniffer;
142   const gchar *uri;
143   GFile *file;
144 
145   ensure_autoquit_off ();
146 
147   g_variant_get (data->parameters,
148                  "(&s)", &uri,
149                  NULL);
150   file = g_file_new_for_uri (uri);
151 
152   print_debug ("Initiating sniff for uri %s", uri);
153 
154   sniffer = cinnamon_mime_sniffer_new (file);
155   cinnamon_mime_sniffer_sniff_async (sniffer,
156                                   sniff_async_ready_cb,
157                                   data);
158 
159   g_object_unref (sniffer);
160   g_object_unref (file);
161 }
162 
163 static void
handle_method_call(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)164 handle_method_call (GDBusConnection       *connection,
165                     const gchar           *sender,
166                     const gchar           *object_path,
167                     const gchar           *interface_name,
168                     const gchar           *method_name,
169                     GVariant              *parameters,
170                     GDBusMethodInvocation *invocation,
171                     gpointer               user_data)
172 {
173   InvocationData *data;
174 
175   data = invocation_data_new (parameters, invocation);
176 
177   if (g_strcmp0 (method_name, "SniffURI") == 0)
178     handle_sniff_uri (data);
179   else
180     g_assert_not_reached ();
181 }
182 
183 static const GDBusInterfaceVTable interface_vtable =
184 {
185   handle_method_call,
186   NULL, /* get_property */
187   NULL, /* set_property */
188 };
189 
190 static void
on_bus_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)191 on_bus_acquired (GDBusConnection *connection,
192                  const gchar *name,
193                  gpointer user_data)
194 {
195   GError *error = NULL;
196 
197   print_debug ("Connected to the session bus: %s", name);
198 
199   g_dbus_connection_register_object (connection,
200                                      "/org/Cinnamon/HotplugSniffer",
201                                      introspection_data->interfaces[0],
202                                      &interface_vtable,
203                                      NULL,
204                                      NULL,
205                                      &error);
206 
207   if (error != NULL)
208     {
209       g_printerr ("Error exporting object on the session bus: %s",
210                   error->message);
211       g_error_free (error);
212 
213       _exit(1);
214     }
215 
216   print_debug ("Object exported on the session bus");
217 }
218 
219 static void
on_name_lost(GDBusConnection * connection,const gchar * name,gpointer user_data)220 on_name_lost (GDBusConnection *connection,
221               const gchar *name,
222               gpointer user_data)
223 {
224   print_debug ("Lost bus name: %s, exiting", name);
225 
226   g_main_loop_quit (loop);
227 }
228 
229 static void
on_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)230 on_name_acquired (GDBusConnection *connection,
231                   const gchar *name,
232                   gpointer user_data)
233 {
234   print_debug ("Acquired bus name: %s", name);
235 }
236 
237 int
main(int argc,char ** argv)238 main (int    argc,
239       char **argv)
240 {
241   guint name_owner_id;
242 
243   introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
244   g_assert (introspection_data != NULL);
245 
246   ensure_autoquit_on ();
247   loop = g_main_loop_new (NULL, FALSE);
248 
249   name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
250                                   BUS_NAME, 0,
251                                   on_bus_acquired,
252                                   on_name_acquired,
253                                   on_name_lost,
254                                   NULL,
255                                   NULL);
256 
257   g_main_loop_run (loop);
258 
259   if (name_owner_id != 0)
260     g_bus_unown_name (name_owner_id);
261 
262   if (loop != NULL)
263     g_main_loop_unref (loop);
264 
265   return 0;
266 }
267 
268 /* ---------------------------------------------------------------------------------------------------- */
269 
270 static void
print_debug(const gchar * format,...)271 print_debug (const gchar *format, ...)
272 {
273   gchar *s;
274   va_list ap;
275   gchar timebuf[64];
276   GTimeVal now;
277   time_t now_t;
278   struct tm broken_down;
279   static volatile gsize once_init_value = 0;
280   static gboolean show_debug = FALSE;
281   static guint pid = 0;
282 
283   if (g_once_init_enter (&once_init_value))
284     {
285       show_debug = (g_getenv ("HOTPLUG_SNIFFER_DEBUG") != NULL);
286       pid = getpid ();
287       g_once_init_leave (&once_init_value, 1);
288     }
289 
290   if (!show_debug)
291     goto out;
292 
293   g_get_current_time (&now);
294   now_t = now.tv_sec;
295   localtime_r (&now_t, &broken_down);
296   strftime (timebuf, sizeof timebuf, "%H:%M:%S", &broken_down);
297 
298   va_start (ap, format);
299   s = g_strdup_vprintf (format, ap);
300   va_end (ap);
301 
302   g_print ("cinnamon-hotplug-sniffer[%d]: %s.%03d: %s\n", pid, timebuf, (gint) (now.tv_usec / 1000), s);
303   g_free (s);
304  out:
305   ;
306 }
307 
308