client library: Use local exception handling; correct API
[moonshot-ui.git] / libmoonshot / libmoonshot-msrpc.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 <windows.h>
36 //#include <rpc.h>
37 #include <msrpc-mingw.h>
38
39 #include "libmoonshot.h"
40 #include "libmoonshot-common.h"
41 #include "moonshot-msrpc.h"
42
43 #include <stdio.h>
44
45 #define MOONSHOT_ENDPOINT_NAME "/org/janet/Moonshot"
46 #define MOONSHOT_INSTALL_PATH_KEY "Software\\Moonshot"
47
48
49 static MoonshotError *moonshot_error_new_from_status (MoonshotErrorCode code,
50                                                       DWORD             status)
51 {
52     MoonshotError *error = malloc (sizeof (MoonshotError));
53     error->code = code;
54     error->message = malloc (256);
55
56     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, (LPSTR)error->message, 255, NULL);
57
58     return error;
59 }
60
61 static void launch_server (MoonshotError **error) {
62     HKEY key = NULL;
63     STARTUPINFO startup_info = { 0 };
64     PROCESS_INFORMATION process_info = { 0 };
65     LONG status;
66     DWORD value_type;
67     DWORD length;
68     char exe_path[1024];
69
70     status = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
71                            MOONSHOT_INSTALL_PATH_KEY,
72                            0,
73                            KEY_READ,
74                            &key);
75
76     if (status != 0) {
77         *error = moonshot_error_new (MOONSHOT_ERROR_OS_ERROR,
78                                      "Unable to read registry key HKLM\\%s",
79                                      MOONSHOT_INSTALL_PATH_KEY);
80         return;
81     }
82
83     length = 1023;
84     status = RegQueryValueEx (key, NULL, NULL, &value_type, exe_path, &length);
85
86     if (value_type != REG_SZ) {
87         *error = moonshot_error_new (MOONSHOT_ERROR_OS_ERROR,
88                                      "Value of registry key HKLM\\%s is invalid. "
89                                      "Please set it to point to the location of "
90                                      "moonshot.exe",
91                                      MOONSHOT_INSTALL_PATH_KEY);
92         return;
93     }
94
95
96     if (status != 0) {
97         *error = moonshot_error_new (MOONSHOT_ERROR_OS_ERROR,
98                                      "Unable to read value of registry key HKLM\\%s",
99                                      MOONSHOT_INSTALL_PATH_KEY);
100         return;
101     }
102
103     startup_info.cb = sizeof (startup_info);
104
105     status = CreateProcess (exe_path, NULL,
106                             NULL, NULL,
107                             FALSE, DETACHED_PROCESS,
108                             NULL, NULL,
109                             &startup_info, &process_info);
110
111     if (status != 0) {
112         *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
113                                      "Unable to spawn the moonshot server at '%s'",
114                                      exe_path);
115         return;
116     }
117 }
118
119 static void bind_rpc (MoonshotError **error)
120 {
121     DWORD status;
122     int   i;
123
124     status = rpc_client_bind (&moonshot_binding_handle,
125                               MOONSHOT_ENDPOINT_NAME,
126                               RPC_PER_USER);
127
128     if (status != RPC_S_OK) {
129         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
130                                                  status);
131         return;
132     }
133
134     status = RpcMgmtIsServerListening (moonshot_binding_handle);
135
136     if (status == RPC_S_NOT_LISTENING) {
137         //launch_server (error);
138
139         /* Allow 5 seconds for the server to launch before we time out */
140         //for (i=0; i<50; i++) {
141            // Sleep (100); /* ms */
142 /*
143             status = RpcMgmtIsServerListening (moonshot_binding_handle);
144
145             if (status == RPC_S_OK)
146                 return;
147
148             if (status != RPC_S_NOT_LISTENING)
149                 break;
150         }*/
151     }
152
153     if (status != RPC_S_OK)
154         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
155                                                  status);
156 }
157
158 static void init_rpc (MoonshotError **error)
159 {
160     static volatile LONG binding_init_flag = 2;
161     int status;
162
163     /* Hack to avoid requiring a moonshot_init() function. Windows does not
164      * provide any synchronisation primitives that can be statically init'ed,
165      * but we can use its atomic variable access functions to achieve the same.
166      * See: http://msdn.microsoft.com/en-us/library/ms684122%28v=vs.85%29.aspx
167      */
168
169     if (binding_init_flag == 0)
170         return;
171
172     if (InterlockedCompareExchange (&binding_init_flag, 1, 2) == 2) {
173         bind_rpc (error);
174
175         /* We'll handle all exceptions locally to avoid interfering with any
176          * other RPC/other exception handling that goes on in the process,
177          * and so we can store the problem in a MooshotError instead of
178          * aborting.
179          */
180         rpc_set_global_exception_handler_enable (FALSE);
181
182         if (InterlockedCompareExchange (&binding_init_flag, 0, 1) != 1) {
183             /* This should never happen */
184             fprintf (stderr, "moonshot: Internal synchronisation error");
185         }
186     } else {
187         while (binding_init_flag != 0)
188             Sleep (100); /* ms */
189     }
190 }
191
192
193 int moonshot_get_identity (const char     *nai,
194                            const char     *password,
195                            const char     *service,
196                            char          **nai_out,
197                            char          **password_out,
198                            char          **server_certificate_hash_out,
199                            char          **ca_certificate_out,
200                            char          **subject_name_constraint_out,
201                            char          **subject_alt_name_constraint_out,
202                            MoonshotError **error)
203 {
204     int success;
205     RpcAsyncCall call;
206
207     init_rpc (error);
208
209     if (*error != NULL)
210         return FALSE;
211
212     rpc_async_call_init (&call);
213
214     nai_out = NULL;
215     password_out = NULL;
216     server_certificate_hash_out = NULL;
217     ca_certificate_out = NULL;
218     subject_name_constraint_out = NULL;
219     subject_alt_name_constraint_out = NULL;
220
221     RPC_TRY_EXCEPT {
222         moonshot_get_identity_rpc (&call,
223                                    nai,
224                                    password,
225                                    service,
226                                    nai_out,
227                                    password_out,
228                                    server_certificate_hash_out,
229                                    ca_certificate_out,
230                                    subject_name_constraint_out,
231                                    subject_alt_name_constraint_out);
232
233         success = rpc_async_call_complete_int (&call);
234     }
235     RPC_EXCEPT {
236         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
237                                                  RPC_GET_EXCEPTION_CODE ());
238     }
239     RPC_END_EXCEPT
240
241     if (*error != NULL)
242         return FALSE;
243
244     if (success == FALSE) {
245         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
246                                      "No identity was returned by the Moonshot "
247                                      "user interface.");
248         return FALSE;
249     }
250
251     return TRUE;
252 }
253
254
255 int moonshot_get_default_identity (char          **nai_out,
256                                    char          **password_out,
257                                    char          **server_certificate_hash_out,
258                                    char          **ca_certificate_out,
259                                    char          **subject_name_constraint_out,
260                                    char          **subject_alt_name_constraint_out,
261                                    MoonshotError **error)
262 {
263     int success;
264     RpcAsyncCall call;
265
266     init_rpc (error);
267
268     if (*error != NULL)
269         return FALSE;
270
271     rpc_async_call_init (&call);
272
273     nai_out = NULL;
274     password_out = NULL;
275     server_certificate_hash_out = NULL;
276     ca_certificate_out = NULL;
277     subject_name_constraint_out = NULL;
278     subject_alt_name_constraint_out = NULL;
279
280     RPC_TRY_EXCEPT {
281         moonshot_get_default_identity_rpc (&call,
282                                            nai_out,
283                                            password_out,
284                                            server_certificate_hash_out,
285                                            ca_certificate_out,
286                                            subject_name_constraint_out,
287                                            subject_alt_name_constraint_out);
288
289         success = rpc_async_call_complete_int (&call);
290     }
291     RPC_EXCEPT {
292         *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
293                                                  RPC_GET_EXCEPTION_CODE ());
294     }
295     RPC_END_EXCEPT
296
297     if (*error != NULL)
298         return FALSE;
299
300     if (success == FALSE) {
301         *error = moonshot_error_new (MOONSHOT_ERROR_NO_IDENTITY_SELECTED,
302                                      "No identity was returned by the Moonshot "
303                                      "user interface.");
304         return FALSE;
305     }
306
307     return TRUE;
308 };