1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2004-2006 Christian Hammond <chipx86@chipx86.com>
4  * Copyright (C) 2004-2006 Mike Hearn <mike@navi.cx>
5  * Copyright (C) 2010 Red Hat, Inc.
6  * Copyright © 2010 Christian Persch
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
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA  02111-1307, USA.
22  */
23 
24 #include "config.h"
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <unistd.h>
30 
31 #include <glib.h>
32 #include <gio/gio.h>
33 
34 #include "notify.h"
35 #include "internal.h"
36 #include "notify-marshal.h"
37 
38 /**
39  * SECTION:notify
40  * @Short_description: Notification API
41  * @Title: notify
42  */
43 
44 static gboolean         _initted = FALSE;
45 static char            *_app_name = NULL;
46 static GDBusProxy      *_proxy = NULL;
47 static GList           *_active_notifications = NULL;
48 static int              _spec_version_major = 0;
49 static int              _spec_version_minor = 0;
50 
51 gboolean
_notify_check_spec_version(int major,int minor)52 _notify_check_spec_version (int major,
53                             int minor)
54 {
55        if (_spec_version_major > major)
56                return TRUE;
57        if (_spec_version_major < major)
58                return FALSE;
59        return _spec_version_minor >= minor;
60 }
61 
62 static gboolean
_notify_get_server_info(char ** ret_name,char ** ret_vendor,char ** ret_version,char ** ret_spec_version,GError ** error)63 _notify_get_server_info (char **ret_name,
64                          char **ret_vendor,
65                          char **ret_version,
66                          char **ret_spec_version,
67                          GError **error)
68 {
69         GDBusProxy *proxy;
70         GVariant   *result;
71 
72         proxy = _notify_get_proxy (error);
73         if (proxy == NULL) {
74                 return FALSE;
75         }
76 
77         result = g_dbus_proxy_call_sync (proxy,
78                                          "GetServerInformation",
79                                          g_variant_new ("()"),
80                                          G_DBUS_CALL_FLAGS_NONE,
81                                          -1 /* FIXME shorter timeout? */,
82                                          NULL,
83                                          error);
84         if (result == NULL) {
85                 return FALSE;
86         }
87         if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(ssss)"))) {
88                 g_variant_unref (result);
89                 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
90                              "Unexpected reply type");
91                 return FALSE;
92         }
93 
94         g_variant_get (result, "(ssss)",
95                        ret_name,
96                        ret_vendor,
97                        ret_version,
98                        ret_spec_version);
99         g_variant_unref (result);
100         return TRUE;
101 }
102 
103 static gboolean
_notify_update_spec_version(GError ** error)104 _notify_update_spec_version (GError **error)
105 {
106        char *spec_version;
107 
108        if (!_notify_get_server_info (NULL, NULL, NULL, &spec_version, error)) {
109                return FALSE;
110        }
111 
112        sscanf (spec_version,
113                "%d.%d",
114                &_spec_version_major,
115                &_spec_version_minor);
116 
117        g_free (spec_version);
118 
119        return TRUE;
120 }
121 
122 
123 /**
124  * notify_set_app_name:
125  * @app_name: The name of the application
126  *
127  * Sets the application name.
128  *
129  */
130 void
notify_set_app_name(const char * app_name)131 notify_set_app_name (const char *app_name)
132 {
133         g_free (_app_name);
134         _app_name = g_strdup (app_name);
135 }
136 
137 /**
138  * notify_init:
139  * @app_name: The name of the application initializing libnotify.
140  *
141  * Initialized libnotify. This must be called before any other functions.
142  *
143  * Returns: %TRUE if successful, or %FALSE on error.
144  */
145 gboolean
notify_init(const char * app_name)146 notify_init (const char *app_name)
147 {
148         g_return_val_if_fail (app_name != NULL, FALSE);
149         g_return_val_if_fail (*app_name != '\0', FALSE);
150 
151         if (_initted)
152                 return TRUE;
153 
154         notify_set_app_name (app_name);
155 
156 #if !GLIB_CHECK_VERSION (2, 36, 0)
157         g_type_init ();
158 #endif
159 
160         _initted = TRUE;
161 
162         return TRUE;
163 }
164 
165 /**
166  * notify_get_app_name:
167  *
168  * Gets the application name registered.
169  *
170  * Returns: The registered application name, passed to notify_init().
171  */
172 const char *
notify_get_app_name(void)173 notify_get_app_name (void)
174 {
175         return _app_name;
176 }
177 
178 /**
179  * notify_uninit:
180  *
181  * Uninitialized libnotify.
182  *
183  * This should be called when the program no longer needs libnotify for
184  * the rest of its lifecycle, typically just before exitting.
185  */
186 void
notify_uninit(void)187 notify_uninit (void)
188 {
189         GList *l;
190 
191         if (!_initted) {
192                 return;
193         }
194 
195         if (_app_name != NULL) {
196                 g_free (_app_name);
197                 _app_name = NULL;
198         }
199 
200         for (l = _active_notifications; l != NULL; l = l->next) {
201                 NotifyNotification *n = NOTIFY_NOTIFICATION (l->data);
202 
203                 if (_notify_notification_get_timeout (n) == 0 ||
204                     _notify_notification_has_nondefault_actions (n)) {
205                         notify_notification_close (n, NULL);
206                 }
207         }
208 
209         if (_proxy != NULL) {
210             g_object_unref (_proxy);
211             _proxy = NULL;
212         }
213 
214         _initted = FALSE;
215 }
216 
217 /**
218  * notify_is_initted:
219  *
220  * Gets whether or not libnotify is initialized.
221  *
222  * Returns: %TRUE if libnotify is initialized, or %FALSE otherwise.
223  */
224 gboolean
notify_is_initted(void)225 notify_is_initted (void)
226 {
227         return _initted;
228 }
229 
230 /*
231  * _notify_get_proxy:
232  * @error: (allow-none): a location to store a #GError, or %NULL
233  *
234  * Synchronously creates the #GDBusProxy for the notification service,
235  * and caches the result.
236  *
237  * Returns: the #GDBusProxy for the notification service, or %NULL on error
238  */
239 GDBusProxy *
_notify_get_proxy(GError ** error)240 _notify_get_proxy (GError **error)
241 {
242         if (_proxy != NULL)
243                 return _proxy;
244 
245         _proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
246                                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
247                                                 NULL,
248                                                 NOTIFY_DBUS_NAME,
249                                                 NOTIFY_DBUS_CORE_OBJECT,
250                                                 NOTIFY_DBUS_CORE_INTERFACE,
251                                                 NULL,
252                                                 error);
253         if (_proxy == NULL) {
254                 return NULL;
255         }
256 
257         if (!_notify_update_spec_version (error)) {
258                g_object_unref (_proxy);
259                _proxy = NULL;
260                return NULL;
261         }
262 
263         g_object_add_weak_pointer (G_OBJECT (_proxy), (gpointer *) &_proxy);
264 
265         return _proxy;
266 }
267 
268 /**
269  * notify_get_server_caps:
270  *
271  * Synchronously queries the server for its capabilities and returns them in a #GList.
272  *
273  * Returns: (transfer full) (element-type utf8): a #GList of server capability strings. Free
274  *   the list elements with g_free() and the list itself with g_list_free().
275  */
276 GList *
notify_get_server_caps(void)277 notify_get_server_caps (void)
278 {
279         GDBusProxy *proxy;
280         GVariant   *result;
281         char      **cap, **caps;
282         GList      *list = NULL;
283 
284         proxy = _notify_get_proxy (NULL);
285         if (proxy == NULL) {
286                 g_warning ("Failed to connect to proxy");
287                 return NULL;
288         }
289 
290         result = g_dbus_proxy_call_sync (proxy,
291                                          "GetCapabilities",
292                                          g_variant_new ("()"),
293                                          G_DBUS_CALL_FLAGS_NONE,
294                                          -1 /* FIXME shorter timeout? */,
295                                          NULL,
296                                          NULL);
297         if (result == NULL) {
298                 return NULL;
299         }
300         if (!g_variant_is_of_type (result, G_VARIANT_TYPE ("(as)"))) {
301                 g_variant_unref (result);
302                 return NULL;
303         }
304 
305         g_variant_get (result, "(^as)", &caps);
306 
307         for (cap = caps; *cap != NULL; cap++) {
308                 list = g_list_prepend (list, *cap);
309         }
310 
311         g_free (caps);
312         g_variant_unref (result);
313 
314         return g_list_reverse (list);
315 }
316 
317 /**
318  * notify_get_server_info:
319  * @ret_name: (out) (allow-none) (transfer full): a location to store the server name, or %NULL
320  * @ret_vendor: (out) (allow-none) (transfer full): a location to store the server vendor, or %NULL
321  * @ret_version: (out) (allow-none) (transfer full): a location to store the server version, or %NULL
322  * @ret_spec_version: (out) (allow-none) (transfer full): a location to store the version the service is compliant with, or %NULL
323  *
324  * Synchronously queries the server for its information, specifically, the name, vendor,
325  * server version, and the version of the notifications specification that it
326  * is compliant with.
327  *
328  * Returns: %TRUE if successful, and the variables passed will be set, %FALSE
329  *          on error. The returned strings must be freed with g_free
330  */
331 gboolean
notify_get_server_info(char ** ret_name,char ** ret_vendor,char ** ret_version,char ** ret_spec_version)332 notify_get_server_info (char **ret_name,
333                         char **ret_vendor,
334                         char **ret_version,
335                         char **ret_spec_version)
336 {
337         return _notify_get_server_info (ret_name, ret_vendor, ret_version, ret_spec_version, NULL);
338 }
339 
340 void
_notify_cache_add_notification(NotifyNotification * n)341 _notify_cache_add_notification (NotifyNotification *n)
342 {
343         _active_notifications = g_list_prepend (_active_notifications, n);
344 }
345 
346 void
_notify_cache_remove_notification(NotifyNotification * n)347 _notify_cache_remove_notification (NotifyNotification *n)
348 {
349         _active_notifications = g_list_remove (_active_notifications, n);
350 }
351