Client library: make MT-safe; add get_default_identity()
[moonshot-ui.git] / libmoonshot / libmoonshot-dbus.c
1 /* libmoonshot - Moonshot client library
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * Author: Sam Thursfield <samthursfield@codethink.co.uk>
33  */
34
35 #include <dbus/dbus-glib.h>
36 #include <dbus/dbus.h>
37
38 #include "libmoonshot.h"
39 #include "libmoonshot-common.h"
40
41 #define MOONSHOT_DBUS_NAME "org.janet.Moonshot"
42 #define MOONSHOT_DBUS_PATH "/org/janet/moonshot"
43
44 /* This library is overly complicated currently due to the requirement
45  * that it work on Debian Squeeze - this has GLib 2.24 which requires us
46  * to use dbus-glib instead of GDBus. If/when this requirement is
47  * dropped the DBus version of the library can be greatly simplified.
48  */
49
50 /* Note that ideally this library would not depend on GLib. This would be
51  * possible using libdbus directly and running our own message loop while
52  * waiting for calls.
53  */
54
55 static DBusGProxy *dbus_connect (MoonshotError **error)
56 {
57     DBusConnection  *connection;
58     DBusError        dbus_error;
59     DBusGConnection *g_connection;
60     DBusGProxy      *g_proxy;
61     GError          *g_error;
62     dbus_bool_t      name_has_owner;
63
64     g_return_val_if_fail (*error == NULL, NULL);
65
66     dbus_error_init (&dbus_error);
67
68     /* Check for moonshot server and start the service if possible. We use
69      * libdbus here because dbus-glib doesn't handle autostarting the service.
70      * If/when we move to GDBus this code can become a one-liner.
71      */
72
73     connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
74
75     if (dbus_error_is_set (&dbus_error)) {
76         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
77                                      "DBus error: %s",
78                                      dbus_error.message);
79         dbus_error_free (&dbus_error);
80         return NULL;
81     }
82
83     name_has_owner  = dbus_bus_name_has_owner (connection,
84                                                MOONSHOT_DBUS_NAME,
85                                                &dbus_error);
86
87     if (dbus_error_is_set (&dbus_error)) {
88         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
89                                      "DBus error: %s",
90                                      dbus_error.message);
91         dbus_error_free (&dbus_error);
92         return NULL;
93     }
94
95     if (! name_has_owner) {
96         dbus_bus_start_service_by_name (connection,
97                                         MOONSHOT_DBUS_NAME,
98                                         0,
99                                         NULL,
100                                         &dbus_error);
101
102         if (dbus_error_is_set (&dbus_error)) {
103             if (strcmp (dbus_error.name + 27, "ServiceUnknown") == 0) {
104                 /* Missing .service file; the moonshot-ui install is broken */
105                 *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
106                                              "The Moonshot service was not found. "
107                                              "Please make sure that moonshot-ui is "
108                                              "correctly installed.");
109             } else {
110                 *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
111                                              "DBus error: %s",
112                                              dbus_error.message);
113             }
114             dbus_error_free (&dbus_error);
115             return NULL;
116         }
117     }
118
119     /* Now the service should be running */
120     g_error = NULL;
121
122     g_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &g_error);
123
124     if (g_error != NULL) {
125         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
126                                      "DBus error: %s",
127                                      g_error->message);
128         g_error_free (g_error);
129         return NULL;
130     }
131
132     g_proxy = dbus_g_proxy_new_for_name_owner (g_connection,
133                                                MOONSHOT_DBUS_NAME,
134                                                MOONSHOT_DBUS_PATH,
135                                                MOONSHOT_DBUS_NAME,
136                                                &g_error);
137
138     if (g_error != NULL) {
139         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
140                                      "DBus error: %s",
141                                      g_error->message);
142         g_error_free (g_error);
143         return NULL;
144     }
145
146     return g_proxy; 
147 }
148
149 static DBusGProxy *get_dbus_proxy (MoonshotError **error)
150 {
151     static DBusGProxy    *dbus_proxy = NULL;
152     static GStaticMutex   init_lock = G_STATIC_MUTEX_INIT;
153
154     g_static_mutex_lock (&init_lock);
155
156     if (dbus_proxy == NULL)
157         dbus_proxy = dbus_connect (error);
158
159     if (dbus_proxy != NULL)
160         g_object_ref (dbus_proxy);
161
162     g_static_mutex_unlock (&init_lock);
163
164     return dbus_proxy;
165 }
166
167 int moonshot_get_identity (const char     *nai,
168                            const char     *password,
169                            const char     *service,
170                            char          **nai_out,
171                            char          **password_out,
172                            char          **server_certificate_hash_out,
173                            char          **ca_certificate_out,
174                            char          **subject_name_constraint_out,
175                            char          **subject_alt_name_constraint_out,
176                            MoonshotError **error)
177 {
178     GError     *g_error = NULL;
179     DBusGProxy *dbus_proxy;
180     int         success;
181
182     dbus_proxy = get_dbus_proxy (error);
183
184     if (*error != NULL)
185         return;
186
187     g_return_if_fail (DBUS_IS_G_PROXY (dbus_proxy));
188
189     dbus_g_proxy_call (dbus_proxy,
190                        "GetIdentity",
191                        &g_error,
192                        G_TYPE_STRING, nai,
193                        G_TYPE_STRING, password,
194                        G_TYPE_STRING, service,
195                        G_TYPE_INVALID,
196                        G_TYPE_STRING, nai_out,
197                        G_TYPE_STRING, password_out,
198                        G_TYPE_STRING, server_certificate_hash_out,
199                        G_TYPE_STRING, ca_certificate_out,
200                        G_TYPE_STRING, subject_name_constraint_out,
201                        G_TYPE_STRING, subject_alt_name_constraint_out,
202                        G_TYPE_BOOLEAN, &success,
203                        G_TYPE_INVALID);
204
205     g_object_unref (dbus_proxy);
206
207     if (g_error != NULL) {
208         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
209                                      g_error->message);
210         return FALSE;
211     }
212
213     if (success == FALSE) {
214         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
215                                      "No identity was returned by the Moonshot "
216                                      "user interface.");
217         return FALSE;
218     }
219
220     return TRUE;
221 }
222
223 int moonshot_get_default_identity (char          **nai_out,
224                                    char          **password_out,
225                                    char          **server_certificate_hash_out,
226                                    char          **ca_certificate_out,
227                                    char          **subject_name_constraint_out,
228                                    char          **subject_alt_name_constraint_out,
229                                    MoonshotError **error)
230 {
231     GError     *g_error = NULL;
232     DBusGProxy *dbus_proxy;
233     int         success = FALSE;
234
235     dbus_proxy = get_dbus_proxy (error);
236
237     if (*error != NULL)
238         return FALSE;
239
240     g_return_if_fail (DBUS_IS_G_PROXY (dbus_proxy));
241
242     dbus_g_proxy_call (dbus_proxy,
243                        "GetDefaultIdentity",
244                        &g_error,
245                        G_TYPE_INVALID,
246                        G_TYPE_STRING, nai_out,
247                        G_TYPE_STRING, password_out,
248                        G_TYPE_STRING, server_certificate_hash_out,
249                        G_TYPE_STRING, ca_certificate_out,
250                        G_TYPE_STRING, subject_name_constraint_out,
251                        G_TYPE_STRING, subject_alt_name_constraint_out,
252                        G_TYPE_BOOLEAN, &success,
253                        G_TYPE_INVALID);
254
255     g_object_unref (dbus_proxy);
256
257     if (g_error != NULL) {
258         *error = moonshot_error_new (MOONSHOT_ERROR_IPC_ERROR,
259                                      g_error->message);
260         return FALSE;
261     }
262
263     if (success == FALSE) {
264         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
265                                      "No identity was returned by the Moonshot "
266                                      "user interface.");
267         return FALSE;
268     }
269
270     return TRUE;
271 }