view connectors/main.c @ 281:c57149ad216e default tip

Copilot-Session: f68442b1-fa8f-46a0-9689-81710613bbd4
author MrJuneJune <me@mrjunejune.com>
date Tue, 18 Aug 2026 22:18:15 -0700
parents b3b547563ec7
children
line wrap: on
line source

#include "connectors/connector.h"
#include "connectors/auth_http_adapter.h"

#include <openssl/crypto.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  char database[1024];
  char auth_database[1024];
  char auth_cookie_secret_hex[AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES * 2 + 1];
  int64 auth_session_idle_ttl;
  char bind[128];
  char port[16];
  char static_dir[1024];
  char client_id[1024];
  char client_secret[2048];
  char redirect_uri[2048];
  char master_key[256];
  uint32 master_key_version;
} Service_Config;

static boolean set_value(Service_Config *config, const char *key, const char *value)
{
#define SET(name, field) if (!strcmp(key, name)) { \
  if (strlen(value) >= sizeof(config->field)) return FALSE; \
  strcpy(config->field, value); return TRUE; \
}
  SET("DATABASE", database)
  SET("DB_PATH", database)
  SET("database", database)
  SET("AUTH_DATABASE", auth_database)
  SET("AUTH_DB_PATH", auth_database)
  SET("auth_database", auth_database)
  SET("AUTH_COOKIE_SECRET", auth_cookie_secret_hex)
  SET("AUTH_COOKIE_SECRET_HEX", auth_cookie_secret_hex)
  SET("auth_cookie_secret_hex", auth_cookie_secret_hex)
  SET("BIND", bind)
  SET("SERVER_HOST", bind)
  SET("bind", bind)
  SET("PORT", port)
  SET("SERVER_PORT", port)
  SET("port", port)
  SET("STATIC_DIR", static_dir)
  SET("static_dir", static_dir)
  SET("GOOGLE_CLIENT_ID", client_id)
  SET("google_client_id", client_id)
  SET("GOOGLE_CLIENT_SECRET", client_secret)
  SET("google_client_secret", client_secret)
  SET("GOOGLE_REDIRECT_URI", redirect_uri)
  SET("google_redirect_uri", redirect_uri)
  SET("MASTER_KEY_BASE64URL", master_key)
  SET("master_key_base64url", master_key)
  if (!strcmp(key, "MASTER_KEY_VERSION") ||
      !strcmp(key, "master_key_version")) {
    config->master_key_version = (uint32)strtoul(value, NULL, 10);
    return config->master_key_version > 0;
  }
  if (!strcmp(key, "AUTH_SESSION_IDLE_TTL") ||
      !strcmp(key, "auth_session_idle_ttl")) {
    char *end = NULL;
    long long parsed = strtoll(value, &end, 10);
    if (!end || *end || parsed <= 0)
      return FALSE;
    config->auth_session_idle_ttl = (int64)parsed;
    return TRUE;
  }
#undef SET
  return TRUE;
}

static boolean load_config(const char *path, Service_Config *config)
{
  FILE *file = fopen(path, "r");
  char workspace_path[2048] = {0};
  if (!file) {
    const char *workspace = getenv("BUILD_WORKSPACE_DIRECTORY");
    if (workspace && workspace[0]) {
      int written = snprintf(
          workspace_path, sizeof(workspace_path), "%s/%s", workspace, path);
      if (written > 0 && (size_t)written < sizeof(workspace_path))
        file = fopen(workspace_path, "r");
    }
  }
  if (!file)
    return FALSE;
  memset(config, 0, sizeof(*config));
  strcpy(config->bind, "127.0.0.1");
  strcpy(config->port, "6981");
  strcpy(config->static_dir, "connectors");
  config->auth_session_idle_ttl = 604800;
  char line[4096];
  boolean ok = TRUE;
  while (ok && fgets(line, sizeof(line), file)) {
    char *newline = strpbrk(line, "\r\n");
    if (newline)
      *newline = '\0';
    if (!line[0] || line[0] == '#')
      continue;
    char *equals = strchr(line, '=');
    if (!equals) {
      ok = FALSE;
      break;
    }
    *equals = '\0';
    ok = set_value(config, line, equals + 1);
  }
  fclose(file);
  return ok && config->database[0] && config->auth_database[0] &&
      config->auth_cookie_secret_hex[0] && config->client_id[0] &&
      config->client_secret[0] && config->redirect_uri[0] &&
      config->master_key[0] && config->master_key_version;
}

int main(int argc, char **argv)
{
  const char *config_path = argc > 1 ? argv[1] : getenv("CONNECTOR_CONFIG_PATH");
  if (!config_path || !config_path[0])
    config_path = "connectors/.config";
  Service_Config service;
  if (!load_config(config_path, &service)) {
    fprintf(stderr, "Invalid connector config: %s\n", config_path);
    return 1;
  }
  Connector_Master_Key key = {.version = service.master_key_version};
  size_t key_length = 0;
  if (!Connector_Base64Url_Decode(
          service.master_key, key.key, sizeof(key.key), &key_length) ||
      key_length != sizeof(key.key)) {
    fprintf(stderr, "MASTER_KEY_BASE64URL must decode to 32 bytes\n");
    return 1;
  }
  Connector_Store store;
  if (!Connector_Store_Open(&store, service.database, &key)) {
    fprintf(stderr, "Unable to open connector database\n");
    return 1;
  }
  Connector_Auth_HTTP_Context auth_context;
  if (!Connector_Auth_HTTP_Init(
          &auth_context, service.auth_database,
          service.auth_cookie_secret_hex, service.auth_session_idle_ttl)) {
    OPENSSL_cleanse(
        service.auth_cookie_secret_hex,
        sizeof(service.auth_cookie_secret_hex));
    fprintf(stderr, "Unable to initialize shared HTTP authentication\n");
    Connector_Store_Close(&store);
    return 1;
  }
  OPENSSL_cleanse(
      service.auth_cookie_secret_hex, sizeof(service.auth_cookie_secret_hex));
  Connector_Google_Config google = {
    .client_id = service.client_id,
    .client_secret = service.client_secret,
    .redirect_uri = service.redirect_uri,
    .oauth_authorize_url = "https://accounts.google.com/o/oauth2/v2/auth",
    .oauth_token_url = "https://oauth2.googleapis.com/token",
    .oauth_revoke_url = "https://oauth2.googleapis.com/revoke",
    .identity_url = "https://openidconnect.googleapis.com/v1/userinfo",
    .drive_api_url = "https://www.googleapis.com",
    .drive_upload_url = "https://www.googleapis.com",
    .gmail_api_url = "https://gmail.googleapis.com"
  };
  Connector_Auth_Adapter auth =
      Connector_Auth_HTTP_Create_Adapter(&auth_context);
  Connector_Service_Configure(&store, &google, auth);
  Seobeo_Router_Init();
  Connector_Service_Register_Routes();
  int result = Seobeo_Web_Server_Start_On(
      service.bind, service.static_dir, service.port, SEOBEO_MODE_EDGE, 4);
  Seobeo_Router_Destroy();
  Connector_Auth_HTTP_Destroy(&auth_context);
  Connector_Store_Close(&store);
  return result;
}