Windows: improve error reporting
[moonshot-ui.git] / libmoonshot / libmoonshot-msrpc.c
index 05000ca..6a2010d 100644 (file)
 #include "libmoonshot-common.h"
 #include "moonshot-msrpc.h"
 
+#include <stdarg.h>
 #include <stdio.h>
+#include <string.h>
 
 #define MOONSHOT_ENDPOINT_NAME "/org/janet/Moonshot"
 #define MOONSHOT_INSTALL_PATH_KEY "Software\\Moonshot"
 
+void *__RPC_USER MIDL_user_allocate (size_t size) {
+    return malloc (size);
+}
+
+void __RPC_USER MIDL_user_free (void *data) {
+    if (data == NULL)
+        return;
+
+    free (data);
+}
+
+void moonshot_free (void *data)
+{
+    free (data);
+}
 
 static MoonshotError *moonshot_error_new_from_status (MoonshotErrorCode code,
                                                       DWORD             status)
@@ -54,6 +71,40 @@ static MoonshotError *moonshot_error_new_from_status (MoonshotErrorCode code,
     error->message = malloc (256);
 
     FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, (LPSTR)error->message, 255, NULL);
+    return error;
+}
+
+static MoonshotError *moonshot_error_new_with_status (MoonshotErrorCode code,
+                                                      DWORD             status,
+                                                      const char        *format, ...)
+{
+    MoonshotError *error = malloc (sizeof (MoonshotError));
+    char *buffer;
+    va_list args;
+    int length;
+
+    va_start (args, format);
+
+    error->code = code;
+
+    buffer = malloc (strlen (format) + 256 + 3);
+    strcpy (buffer, format);
+    strcat (buffer, ": ");
+
+    FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
+                   NULL,
+                   status,
+                   0,
+                   (LPSTR)buffer + strlen (format) + 3,
+                   255,
+                   NULL);
+
+    length = _vscprintf (buffer, args);
+    error->message = malloc (length + 1);
+    _vsnprintf (error->message, length, buffer, args);
+    free (buffer);
+
+    va_end (args);
 
     return error;
 }
@@ -63,6 +114,7 @@ static void launch_server (MoonshotError **error) {
     STARTUPINFO startup_info = { 0 };
     PROCESS_INFORMATION process_info = { 0 };
     LONG status;
+    BOOL success;
     DWORD value_type;
     DWORD length;
     char exe_path[1024];
@@ -73,45 +125,62 @@ static void launch_server (MoonshotError **error) {
                            KEY_READ,
                            &key);
 
-    if (status != 0) {
-        *error = moonshot_error_new (MOONSHOT_ERROR_OS_ERROR,
-                                     "Unable to read registry key HKLM\\%s",
-                                     MOONSHOT_INSTALL_PATH_KEY);
+    if (status == ERROR_FILE_NOT_FOUND) {
+        *error = moonshot_error_new
+                   (MOONSHOT_ERROR_INSTALLATION_ERROR,
+                    "Moonshot is not installed correctly on this system. "
+                    "(Registry key HKLM\\%s was not found).",
+                    MOONSHOT_INSTALL_PATH_KEY);
+        return;
+    } else if (status != 0) {
+        *error = moonshot_error_new_with_status
+                   (MOONSHOT_ERROR_OS_ERROR,
+                    status,
+                    "Unable to read registry key HKLM\\%s",
+                    MOONSHOT_INSTALL_PATH_KEY);
         return;
     }
 
     length = 1023;
-    status = RegQueryValueEx (key, NULL, NULL, &value_type, exe_path, &length);
+    status = RegQueryValueEx (key, NULL, NULL, &value_type, (LPBYTE )exe_path, &length);
 
     if (value_type != REG_SZ) {
-        *error = moonshot_error_new (MOONSHOT_ERROR_INSTALLATION_ERROR,
-                                     "Value of registry key HKLM\\%s is invalid. "
-                                     "Please set it to point to the location of "
-                                     "moonshot.exe",
-                                     MOONSHOT_INSTALL_PATH_KEY);
+        *error = moonshot_error_new_with_status
+                   (MOONSHOT_ERROR_INSTALLATION_ERROR,
+                    status,
+                    "Value of registry key HKLM\\%s is invalid. Please set it "
+                    "to point to the location of moonshot.exe",
+                    MOONSHOT_INSTALL_PATH_KEY);
         return;
     }
 
 
     if (status != 0) {
-        *error = moonshot_error_new (MOONSHOT_ERROR_OS_ERROR,
-                                     "Unable to read value of registry key HKLM\\%s",
-                                     MOONSHOT_INSTALL_PATH_KEY);
+        *error = moonshot_error_new_with_status
+                   (MOONSHOT_ERROR_OS_ERROR,
+                    status,
+                    "Unable to read value of registry key HKLM\\%s",
+                    MOONSHOT_INSTALL_PATH_KEY);
         return;
     }
 
     startup_info.cb = sizeof (startup_info);
-
-    status = CreateProcess (exe_path, NULL,
-                            NULL, NULL,
-                            FALSE, DETACHED_PROCESS,
-                            NULL, NULL,
-                            &startup_info, &process_info);
-
-    if (status != 0) {
-        *error = moonshot_error_new (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
-                                     "Unable to spawn the moonshot server at '%s'",
-                                     exe_path);
+    success = CreateProcess (exe_path,
+                             NULL,
+                             NULL,
+                             NULL,
+                             TRUE,
+                             DETACHED_PROCESS,
+                             NULL,
+                             NULL,
+                             &startup_info, &process_info);
+
+    if (! success) {
+        *error = moonshot_error_new_with_status
+                   (MOONSHOT_ERROR_UNABLE_TO_START_SERVICE,
+                    GetLastError (),
+                    "Unable to spawn the moonshot server at '%s'",
+                    exe_path);
         return;
     }
 }
@@ -134,12 +203,15 @@ static void bind_rpc (MoonshotError **error)
     status = RpcMgmtIsServerListening (moonshot_binding_handle);
 
     if (status == RPC_S_NOT_LISTENING) {
-        //launch_server (error);
+        launch_server (error);
+
+        if (*error != NULL)
+            return;
+
+        /* Allow 1 minute for the server to launch before we time out */
+        for (i=0; i<600; i++) {
+            Sleep (100); /* ms */
 
-        /* Allow 5 seconds for the server to launch before we time out */
-        //for (i=0; i<50; i++) {
-           // Sleep (100); /* ms */
-/*
             status = RpcMgmtIsServerListening (moonshot_binding_handle);
 
             if (status == RPC_S_OK)
@@ -147,7 +219,7 @@ static void bind_rpc (MoonshotError **error)
 
             if (status != RPC_S_NOT_LISTENING)
                 break;
-        }*/
+        }
     }
 
     if (status != RPC_S_OK)
@@ -158,7 +230,6 @@ static void bind_rpc (MoonshotError **error)
 static void init_rpc (MoonshotError **error)
 {
     static volatile LONG binding_init_flag = 2;
-    int status;
 
     /* Hack to avoid requiring a moonshot_init() function. Windows does not
      * provide any synchronisation primitives that can be statically init'ed,
@@ -201,7 +272,7 @@ int moonshot_get_identity (const char     *nai,
                            char          **subject_alt_name_constraint_out,
                            MoonshotError **error)
 {
-    int success;
+    int success = FALSE;
     RpcAsyncCall call;
 
     init_rpc (error);
@@ -211,12 +282,16 @@ int moonshot_get_identity (const char     *nai,
 
     rpc_async_call_init (&call);
 
-    nai_out = NULL;
-    password_out = NULL;
-    server_certificate_hash_out = NULL;
-    ca_certificate_out = NULL;
-    subject_name_constraint_out = NULL;
-    subject_alt_name_constraint_out = NULL;
+    if (nai == NULL) nai = "";
+    if (password == NULL) password = "";
+    if (service == NULL) service = "";
+
+    *nai_out = NULL;
+    *password_out = NULL;
+    *server_certificate_hash_out = NULL;
+    *ca_certificate_out = NULL;
+    *subject_name_constraint_out = NULL;
+    *subject_alt_name_constraint_out = NULL;
 
     RPC_TRY_EXCEPT {
         moonshot_get_identity_rpc (&call,
@@ -260,7 +335,7 @@ int moonshot_get_default_identity (char          **nai_out,
                                    char          **subject_alt_name_constraint_out,
                                    MoonshotError **error)
 {
-    int success;
+    int success = FALSE;
     RpcAsyncCall call;
 
     init_rpc (error);
@@ -270,12 +345,12 @@ int moonshot_get_default_identity (char          **nai_out,
 
     rpc_async_call_init (&call);
 
-    nai_out = NULL;
-    password_out = NULL;
-    server_certificate_hash_out = NULL;
-    ca_certificate_out = NULL;
-    subject_name_constraint_out = NULL;
-    subject_alt_name_constraint_out = NULL;
+    *nai_out = NULL;
+    *password_out = NULL;
+    *server_certificate_hash_out = NULL;
+    *ca_certificate_out = NULL;
+    *subject_name_constraint_out = NULL;
+    *subject_alt_name_constraint_out = NULL;
 
     RPC_TRY_EXCEPT {
         moonshot_get_default_identity_rpc (&call,
@@ -306,3 +381,77 @@ int moonshot_get_default_identity (char          **nai_out,
 
     return TRUE;
 };
+
+int moonshot_install_id_card (const char     *display_name,
+                              const char     *user_name,
+                              const char     *password,
+                              const char     *realm,
+                              char           *rules_patterns[],
+                              int             rules_patterns_length,
+                              char           *rules_always_confirm[],
+                              int             rules_always_confirm_length,
+                              char           *services[],
+                              int             services_length,
+                              const char     *ca_cert,
+                              const char     *subject,
+                              const char     *subject_alt,
+                              const char     *server_cert,
+                              int             force_flat_file_store,
+                              MoonshotError **error)
+{
+    int success = FALSE;
+
+    init_rpc (error);
+    if (*error != NULL)
+        return FALSE;
+
+    if (user_name == NULL) user_name = "";
+    if (password == NULL) password = "";
+    if (realm == NULL) realm = "";
+    if (ca_cert == NULL) ca_cert = "";
+    if (subject == NULL) subject = "";
+    if (subject_alt == NULL) subject_alt = "";
+    if (server_cert == NULL) server_cert = "";
+
+    RPC_TRY_EXCEPT {
+        success = moonshot_install_id_card_rpc (display_name,
+                                                user_name,
+                                                password,
+                                                realm,
+                                                rules_patterns,
+                                                rules_patterns_length,
+                                                rules_always_confirm,
+                                                rules_always_confirm_length,
+                                                services,
+                                                services_length,
+                                                ca_cert,
+                                                subject,
+                                                subject_alt,
+                                                server_cert,
+                                                force_flat_file_store);
+    }
+    RPC_EXCEPT {
+        *error = moonshot_error_new_from_status (MOONSHOT_ERROR_IPC_ERROR,
+                                                 RPC_GET_EXCEPTION_CODE ());
+    }
+    RPC_END_EXCEPT
+    return success;
+}
+
+BOOL WINAPI DllMain (HINSTANCE  hinst,
+                     DWORD      reason,
+                     void      *reserved)
+{
+    if (reason == DLL_PROCESS_DETACH) {
+        /* Process exiting/DLL being unloaded. This is a good
+         * opportunity to free the RPC binding.
+         *
+         * FIXME: we can't use the msrpc-mingw routine for this in case
+         * it was already unloaded. I'd love to work out how to link
+         * that library statically into libmoonshot-0.dll.
+         */
+        RpcBindingFree (&moonshot_binding_handle);
+    }
+
+    return TRUE;
+}