fcd19b4c7fe2b85815a5c2325cd1ec37f90bef7c
[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
40 #define MOONSHOT_DBUS_NAME "org.janet.Moonshot"
41 #define MOONSHOT_DBUS_PATH "/org/janet/moonshot"
42
43 /* This library is overly complicated currently due to the requirement
44  * that it work on Debian Squeeze - this has GLib 2.24 which requires us
45  * to use dbus-glib instead of GDBus. If/when this requirement is
46  * dropped the DBus version of the library can be greatly simplified.
47  */
48
49 typedef struct {
50     char *nai;
51     char *password;
52     char *server_certificate_hash;
53     char *ca_certificate;
54     char *subject_name_constraint;
55     char *subject_alt_name_constraint;
56 } MoonshotIdentityData;
57
58 static MoonshotIdentityData *moonshot_identity_data_new ()
59 {
60     return g_slice_new (MoonshotIdentityData);
61 }
62
63 static void moonshot_identity_data_free (void *data)
64 {
65     g_slice_free (MoonshotIdentityData, data);
66 }
67
68 static DBusGProxy *moonshot_dbus_proxy = NULL;
69
70 GQuark moonshot_error_quark (void)
71 {
72     return g_quark_from_static_string ("moonshot-error-quark");
73 }
74
75 static DBusGProxy *dbus_connect (GError **g_error)
76 {
77     DBusConnection  *connection;
78     DBusError        dbus_error;
79     DBusGConnection *g_connection;
80     DBusGProxy      *g_proxy;
81     dbus_bool_t      name_has_owner;
82
83     g_return_val_if_fail (*g_error == NULL, NULL);
84
85     dbus_error_init (&dbus_error);
86
87     /* Check for moonshot server and start the service if possible. We use
88      * libdbus here because dbus-glib doesn't handle autostarting the service.
89      * If/when we move to GDBus this code can become a one-liner.
90      */
91
92     connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
93
94     if (dbus_error_is_set (&dbus_error)) {
95         *g_error = g_error_new (MOONSHOT_ERROR,
96                                 MOONSHOT_ERROR_DBUS_ERROR,
97                                 "DBus error: %s",
98                                 dbus_error.message);
99         dbus_error_free (&dbus_error);
100         return NULL;
101     }
102
103     name_has_owner  = dbus_bus_name_has_owner (connection,
104                                                MOONSHOT_DBUS_NAME,
105                                                &dbus_error);
106
107     if (dbus_error_is_set (&dbus_error)) {
108         *g_error = g_error_new (MOONSHOT_ERROR,
109                                 MOONSHOT_ERROR_DBUS_ERROR,
110                                 "DBus error: %s",
111                                 dbus_error.message);
112
113         dbus_error_free (&dbus_error);
114         return NULL;
115     }
116
117     if (! name_has_owner) {
118         dbus_bus_start_service_by_name (connection,
119                                         MOONSHOT_DBUS_NAME,
120                                         0,
121                                         NULL,
122                                         &dbus_error);
123
124         if (dbus_error_is_set (&dbus_error)) {
125             if (strcmp (dbus_error.name + 27, "ServiceUnknown") == 0) {
126                 /* Missing .service file; the moonshot-ui install is broken */
127                 *g_error = g_error_new (MOONSHOT_ERROR,
128                                         MOONSHOT_ERROR_SERVICE_NOT_FOUND,
129                                         "The Moonshot service was not found. "
130                                         "Please make sure that moonshot-ui is "
131                                         "correctly installed.");
132             } else {
133                 *g_error = g_error_new (MOONSHOT_ERROR,
134                                         MOONSHOT_ERROR_DBUS_ERROR,
135                                         "DBus error: %s",
136                                         dbus_error.message);
137             }
138             dbus_error_free (&dbus_error);
139             return NULL;
140         }
141     }
142
143     /* Now the service should be running */
144
145     g_connection = dbus_g_bus_get (DBUS_BUS_SESSION, g_error);
146
147     if (*g_error != NULL)
148         return NULL;
149
150     g_proxy = dbus_g_proxy_new_for_name_owner (g_connection,
151                                                MOONSHOT_DBUS_NAME,
152                                                MOONSHOT_DBUS_PATH,
153                                                MOONSHOT_DBUS_NAME,
154                                                g_error);
155
156     return g_proxy; 
157 }
158
159 static void dbus_call_complete_cb (DBusGProxy     *proxy,
160                                    DBusGProxyCall *call_id,
161                                    void           *user_data)
162 {
163     GError *error = NULL;
164     GSimpleAsyncResult   *token;
165     MoonshotIdentityData *identity_data;
166     gboolean              success;
167
168     token = G_SIMPLE_ASYNC_RESULT (user_data);
169     identity_data = moonshot_identity_data_new ();
170
171     dbus_g_proxy_end_call (moonshot_dbus_proxy,
172                            call_id,
173                            &error,
174                            G_TYPE_STRING, &identity_data->nai,
175                            G_TYPE_STRING, &identity_data->password,
176                            G_TYPE_STRING, &identity_data->server_certificate_hash,
177                            G_TYPE_STRING, &identity_data->ca_certificate,
178                            G_TYPE_STRING, &identity_data->subject_name_constraint,
179                            G_TYPE_STRING, &identity_data->subject_alt_name_constraint,
180                            G_TYPE_BOOLEAN, &success,
181                            G_TYPE_INVALID);
182
183     if (error != NULL) {
184         g_simple_async_result_set_from_error (token, error);
185     }
186     else
187     if (success == FALSE) {
188         error = g_error_new (MOONSHOT_ERROR,
189                              MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
190                              "No matching identity was available");
191         g_simple_async_result_set_from_error (token, error);
192         g_error_free (error);
193     }
194     else {        
195         g_simple_async_result_set_op_res_gpointer (token,
196                                                    identity_data,
197                                                    moonshot_identity_data_free);
198     }
199
200     g_simple_async_result_complete (token);
201     g_object_unref (token);
202 }
203
204 /**
205  * moonshot_get_identity:
206  * @cancellable: A #GCancellable, or %NULL.
207  * @callback: A #GAsyncReadyCallback, which will be called when the
208  *            operation completes, fails or is cancelled.
209  * @user_data: Data to pass to @callback
210  * @nai: Name and issuer constraint for the required identity, or %NULL.
211  * @password: Password for the identity, or %NULL.
212  * @service: Service constraint for the required identity, or %NULL.
213  *
214  * This function initiates a call to the Moonshot server to request an ID card.
215  * The server will be activated if it is not already running. The user interface
216  * will be displayed if there is more than one matching identity and the user 
217  * will be asked to select one.
218  *
219  * When an identity has been selected, or the operation fails or is cancelled,
220  * @callback will be run.
221  *
222  * Note that the actual IPC call may not be made until control returns to the
223  * GLib main loop.
224  */
225 void moonshot_get_identity (GCancellable        *cancellable,
226                             GAsyncReadyCallback  callback,
227                             gpointer             user_data,
228                             const char          *nai,
229                             const char          *password,
230                             const char          *service)
231 {
232     DBusGProxyCall     *call_id;
233     GSimpleAsyncResult *result; 
234     GError *error = NULL;
235
236     if (moonshot_dbus_proxy == NULL)
237         moonshot_dbus_proxy = dbus_connect (&error);
238
239     if (moonshot_dbus_proxy == NULL) {
240         result = g_simple_async_result_new (NULL,
241                                             callback,
242                                             user_data,
243                                             moonshot_get_identity);
244         g_simple_async_result_set_from_error (result, error);
245         g_simple_async_result_complete_in_idle (result);
246         g_error_free (error);
247         return;
248     }
249
250     g_return_if_fail (DBUS_IS_G_PROXY (moonshot_dbus_proxy));
251
252     result = g_simple_async_result_new (NULL,
253                                         callback,
254                                         user_data,
255                                         moonshot_get_identity);
256
257     call_id = dbus_g_proxy_begin_call (moonshot_dbus_proxy,
258                                        "GetIdentity",
259                                        dbus_call_complete_cb,
260                                        result, NULL,
261                                        G_TYPE_STRING, nai,
262                                        G_TYPE_STRING, password,
263                                        G_TYPE_STRING, service);
264 }
265
266 /**
267  * moonshot_get_identity_finish:
268  * @result: The #GAsyncResult which was passed to your callback.
269  * @nai: A pointer to a string which receives the name and issuer of the
270  *       selected identity.
271  * @password: A pointer to a string which receives the password.
272  * @server_certificate_hash: Receives a hash of the identity server's
273  *                           certificate, or %NULL.
274  * @ca_certificate: The CA certificate, if @server_certificate_hash was %NULL.
275  * @subject_name_constraint: Set if @ca_certificate is set, otherwise %NULL.
276  * @subject_alt_name_constraint: Set if @ca_certificate is set, otherwise %NULL.
277  * @error: Return location for an error, or %NULL.
278  *
279  * Gets the details of the identity card that was selected, if any.
280  *
281  * There are two types of trust anchor that may be returned. If
282  * @server_certificate_hash is non-empty, the remaining parameters will be
283  * empty. Otherwise, the @ca_certificate parameter and the subject name
284  * constraints will be returned.
285  *
286  * Return value: %TRUE if an identity was successfully selected, %FALSE on
287  *               failure.
288  */
289 gboolean moonshot_get_identity_finish (GAsyncResult  *result,
290                                        char         **nai,
291                                        char         **password,
292                                        char         **server_certificate_hash,
293                                        char         **ca_certificate,
294                                        char         **subject_name_constraint,
295                                        char         **subject_alt_name_constraint,
296                                        GError       **error)
297 {
298     MoonshotIdentityData *identity;
299
300     g_return_val_if_fail (g_simple_async_result_is_valid (result,
301                                                           NULL,
302                                                           moonshot_get_identity),
303                           FALSE);
304
305     if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
306         return FALSE;
307
308     identity = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
309
310     *nai = identity->nai;
311     *password = identity->password;
312     *server_certificate_hash = identity->server_certificate_hash;
313     *ca_certificate = identity->ca_certificate;
314     *subject_name_constraint = identity->subject_name_constraint;
315     *subject_alt_name_constraint = identity->subject_alt_name_constraint;
316
317     return TRUE;
318 }
319
320
321     /**
322      * Returns the default identity - most recently used.
323      *
324      * @param nai_out NAI stored in the ID card
325      * @param password_out Password stored in the ID card
326      *
327      * @return true on success, false if no identities are stored
328      */