Repository: google/glome (language: python)

Issue:
Rewrite glome-login's config module to use glib
Now that we agreed to use glib, we might want to consider moving away from libconfuse to something more solid/understandable like [glib's Key-value file parser](https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html).


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index c2b04981..f1251eef 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,6 @@ Building the GLOME library requires
  - Meson >=0.49.2
  - OpenSSL headers >=1.1.1
  - glib-2.0 (for glome-login as well as tests)
- - libconfuse (for glome-login)
  - libpam (for PAM module)
 
 ### Instructions
diff --git a/kokoro/alpine/fetch_dependencies.sh b/kokoro/alpine/fetch_dependencies.sh
index 6bc81030..9ac650c4 100755
--- a/kokoro/alpine/fetch_dependencies.sh
+++ b/kokoro/alpine/fetch_dependencies.sh
@@ -3,4 +3,4 @@ set -e
 
 apk add --no-cache \
   alpine-sdk meson \
-  openssl-dev confuse-dev glib-dev linux-pam-dev
+  openssl-dev glib-dev linux-pam-dev
diff --git a/kokoro/rodete/fetch_dependencies.sh b/kokoro/rodete/fetch_dependencies.sh
index d7b397dd..2a30d69c 100755
--- a/kokoro/rodete/fetch_dependencies.sh
+++ b/kokoro/rodete/fetch_dependencies.sh
@@ -5,4 +5,4 @@ export DEBIAN_FRONTEND=noninteractive
 apt-get update
 apt-get install -y \
   build-essential meson pkg-config \
-  libssl-dev libconfuse-dev libglib2.0-dev libpam0g-dev libpam-wrapper
+  libssl-dev libglib2.0-dev libpam0g-dev libpam-wrapper
diff --git a/login/config.c b/login/config.c
index 10286789..bca6d267 100644
--- a/login/config.c
+++ b/login/config.c
@@ -13,79 +13,55 @@
 // limitations under the License.
 
 #include <alloca.h>
-#include <confuse.h>
+#include <glib.h>
 #include <string.h>
 
 #include "ui.h"
 
-static void print_config_error(cfg_t* cfg, const char* fmt, va_list ap) {
-  char* format = alloca(7 + strlen(fmt) + 1);
-  strcpy(format, "ERROR: ");
-  strcat(format, fmt);
-  errorf(format, ap);
-}
-
 int parse_config_file(login_config_t* config) {
-  char* service_key = NULL;
-  long service_key_version = 0;
-  char* url_prefix = NULL;
-  // clang-format off
-  cfg_opt_t service_opts[] = {
-      CFG_SIMPLE_STR("key", &service_key),  // Hex-encoded service key
-      CFG_SIMPLE_INT("key-version", &service_key_version),  // Key version
-      CFG_SIMPLE_STR("url-prefix", &url_prefix),  // HTTP URL prefix of web service
-      CFG_END()
-  };
-  cfg_opt_t opts[] = {
-    CFG_SEC("service", service_opts, CFGF_NONE),
-    CFG_END()
-  };
-  // clang-format on
-  cfg_t* cfg = cfg_init(opts, CFGF_NONE);
-  cfg_set_error_function(cfg, print_config_error);
+  g_autofree gchar* service_key = NULL;
+  guint64 service_key_version = 0;
+  g_autofree gchar* url_prefix = NULL;
+  g_autoptr(GKeyFile) cfg = g_key_file_new();
+  g_autoptr(GError) error = NULL;
 
-  int required = config->config_path != NULL;
+  bool required = config->config_path != NULL;
   if (!required) {
     config->config_path = DEFAULT_CONFIG_FILE;
   }
 
-  int r = cfg_parse(cfg, config->config_path);
-  if (required && r == CFG_FILE_ERROR) {
-    perror("ERROR: config file could not be read");
-    cfg_free(cfg);
+  gboolean loaded = g_key_file_load_from_file(cfg, config->config_path,
+                                              G_KEY_FILE_NONE, &error);
+  if (required && !loaded) {
+    errorf("ERROR: config file could not be read: %s\n", error->message);
     return -1;
-  } else if (r == CFG_PARSE_ERROR) {
-    // print_config_error will have been invoked by cfg_parse to print the
-    // error encountered.
-    cfg_free(cfg);
-    return -2;
   }
 
-  cfg_t* cfg_service = cfg_getsec(cfg, "service");
-  if (cfg_service != NULL) {
-    if (service_key != NULL &&
-        is_zeroed(config->service_key, sizeof config->service_key)) {
-      if (decode_hex(config->service_key, sizeof config->service_key,
-                     service_key)) {
-        errorf("ERROR: Failed to hex decode service key\n");
-        cfg_free(cfg);
-        return -3;
-      }
-    }
-    if (service_key_version > 255) {
-      errorf("ERROR: Key version %ld too large, must fit into 8-bit int\n",
-             service_key_version);
-      cfg_free(cfg);
-      return -4;
-    }
-    if (service_key_version > 0 && config->service_key_id == 0) {
-      config->service_key_id = service_key_version;
-    }
-    if (url_prefix != NULL && config->url_prefix == NULL) {
-      config->url_prefix = strdup(url_prefix);
+  service_key = g_key_file_get_value(cfg, "service", "key", NULL);
+  if (service_key != NULL &&
+      is_zeroed(config->service_key, sizeof config->service_key)) {
+    if (decode_hex(config->service_key, sizeof config->service_key,
+                   service_key)) {
+      errorf("ERROR: Failed to hex decode service key\n");
+      return -2;
     }
   }
 
-  cfg_free(cfg);
+  service_key_version =
+      g_key_file_get_uint64(cfg, "service", "key-version", NULL);
+  if (service_key_version > 255) {
+    errorf("ERROR: Key version %ld too large, must fit into 8-bit int\n",
+           service_key_version);
+    return -3;
+  }
+  if (service_key_version > 0 && config->service_key_id == 0) {
+    config->service_key_id = service_key_version;
+  }
+
+  url_prefix = g_key_file_get_value(cfg, "service", "url-prefix", NULL);
+  if (url_prefix != NULL && config->url_prefix == NULL) {
+    config->url_prefix = strdup(url_prefix);
+  }
+
   return 0;
 }
diff --git a/login/example.cfg b/login/example.cfg
index 6f1e66c8..0356221f 100644
--- a/login/example.cfg
+++ b/login/example.cfg
@@ -1,12 +1,10 @@
-service
-{
-  # Replace this with your own GLOME server-side public key.
-  #
-  # This can be generated as any other GLOME key, where the private key is
-  # given to the server (glome.example.com in this example) and the public key
-  # is distributed to all clients through this configuration file.
-  #
-  #key = 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
-  key-version = 1
-  url-prefix = "https://glome.example.com"
-}
+[service]
+# Replace this with your own GLOME server-side public key.
+#
+# This can be generated as any other GLOME key, where the private key is
+# given to the server (glome.example.com in this example) and the public key
+# is distributed to all clients through this configuration file.
+#
+#key = 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
+key-version = 1
+url-prefix = https://glome.example.com
diff --git a/login/meson.build b/login/meson.build
index 7f40fafe..140259aa 100644
--- a/login/meson.build
+++ b/login/meson.build
@@ -12,8 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-confuse_dep = dependency('libconfuse')
-
 install_data(
   'example.cfg',
   rename : 'config',
@@ -34,7 +32,7 @@ login_lib = static_library(
         'ui.c',
         'ui.h',
     ],
-    dependencies : [openssl_dep, glib_dep, confuse_dep],
+    dependencies : [openssl_dep, glib_dep],
     link_with : glome_lib,
     include_directories : glome_incdir,
     install : false)


Test patch:
diff --git a/login/pam_test.c b/login/pam_test.c
index 66f6e1ce..3ccc6bb8 100644
--- a/login/pam_test.c
+++ b/login/pam_test.c
@@ -131,12 +131,11 @@ int test_config() {
   }
 
   fprintf(f,
-          "service\n{\n"
+          "[service]\n"
           "key = "
           "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f\n"
           "key-version = 1\n"
-          "url-prefix = \"https://test.service\"\n"
-          "}\n");
+          "url-prefix = https://test.service\n");
   fclose(f);
 
   len = strlen(runtime_dir) + 1 + strlen(service) + 1;