seafile-server/common/seaf-utils.c
feiniks ed6a292061
Read database config from seafile.conf and env (#713)
* Read database config from seafile.conf and env

* Go read database config from seafile.conf and env

* Delete ccnet.conf

* Don't need ccnet_db_name config

* Add ccnet db name env

* Delete unix_socket and don't return error when failed to read from seafile.conf

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
2024-11-05 17:42:24 +08:00

514 lines
13 KiB
C

#include "common.h"
#include "log.h"
#include "seafile-session.h"
#include "seaf-utils.h"
#include "seaf-db.h"
#include "utils.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <jwt.h>
#define JWT_TOKEN_EXPIRE_TIME 3*24*3600 /* 3 days*/
char *
seafile_session_get_tmp_file_path (SeafileSession *session,
const char *basename,
char path[])
{
int path_len;
path_len = strlen (session->tmp_file_dir);
memcpy (path, session->tmp_file_dir, path_len + 1);
path[path_len] = '/';
strcpy (path + path_len + 1, basename);
return path;
}
#define DEFAULT_MAX_CONNECTIONS 100
#define SQLITE_DB_NAME "seafile.db"
#define CCNET_DB "ccnet.db"
static int
sqlite_db_start (SeafileSession *session)
{
char *db_path;
int max_connections = 0;
max_connections = g_key_file_get_integer (session->config,
"database", "max_connections",
NULL);
if (max_connections <= 0)
max_connections = DEFAULT_MAX_CONNECTIONS;
db_path = g_build_filename (session->seaf_dir, SQLITE_DB_NAME, NULL);
session->db = seaf_db_new_sqlite (db_path, max_connections);
if (!session->db) {
seaf_warning ("Failed to start sqlite db.\n");
return -1;
}
return 0;
}
#ifdef HAVE_MYSQL
#define MYSQL_DEFAULT_PORT 3306
typedef struct DBOption {
char *user;
char *passwd;
char *host;
char *ca_path;
char *charset;
char *ccnet_db_name;
char *seafile_db_name;
gboolean use_ssl;
gboolean skip_verify;
int port;
int max_connections;
} DBOption;
static void
db_option_free (DBOption *option)
{
if (!option)
return;
g_free (option->user);
g_free (option->passwd);
g_free (option->host);
g_free (option->ca_path);
g_free (option->charset);
g_free (option->ccnet_db_name);
g_free (option->seafile_db_name);
g_free (option);
}
static int
load_db_option_from_env (DBOption *option)
{
const char *env_user, *env_passwd, *env_host, *env_ccnet_db, *env_seafile_db;
env_user = g_getenv("SEAFILE_MYSQL_DB_USER");
env_passwd = g_getenv("SEAFILE_MYSQL_DB_PASSWORD");
env_host = g_getenv("SEAFILE_MYSQL_DB_HOST");
env_ccnet_db = g_getenv("SEAFILE_MYSQL_DB_CCNET_DB_NAME");
env_seafile_db = g_getenv("SEAFILE_MYSQL_DB_SEAFILE_DB_NAME");
if (env_user) {
g_free (option->user);
option->user = g_strdup (env_user);
}
if (env_passwd) {
g_free (option->passwd);
option->passwd = g_strdup (env_passwd);
}
if (env_host) {
g_free (option->host);
option->host = g_strdup (env_host);
}
if (env_ccnet_db) {
g_free (option->ccnet_db_name);
option->ccnet_db_name = g_strdup (env_ccnet_db);
} else if (!option->ccnet_db_name) {
option->ccnet_db_name = g_strdup ("ccnet_db");
seaf_message ("Failed to read SEAFILE_MYSQL_DB_CCNET_DB_NAME, use ccnet_db by default");
}
if (env_seafile_db) {
g_free (option->seafile_db_name);
option->seafile_db_name = g_strdup (env_seafile_db);
} else if (!option->seafile_db_name) {
option->seafile_db_name = g_strdup ("seafile_db");
seaf_message ("Failed to read SEAFILE_MYSQL_DB_SEAFILE_DB_NAME, use seafile_db by default");
}
return 0;
}
static DBOption *
load_db_option (SeafileSession *session)
{
GError *error = NULL;
int ret = 0;
DBOption *option = g_new0 (DBOption, 1);
option->host = seaf_key_file_get_string (session->config, "database", "host", NULL);
option->port = g_key_file_get_integer (session->config, "database", "port", &error);
if (error) {
g_clear_error (&error);
option->port = MYSQL_DEFAULT_PORT;
}
option->user = seaf_key_file_get_string (session->config, "database", "user", NULL);
option->passwd = seaf_key_file_get_string (session->config, "database", "password", NULL);
option->seafile_db_name = seaf_key_file_get_string (session->config, "database", "db_name", NULL);
option->use_ssl = g_key_file_get_boolean (session->config,
"database", "use_ssl", NULL);
option->skip_verify = g_key_file_get_boolean (session->config,
"database", "skip_verify", NULL);
if (option->use_ssl && !option->skip_verify) {
option->ca_path = seaf_key_file_get_string (session->config,
"database", "ca_path", NULL);
if (!option->ca_path) {
seaf_warning ("ca_path is required if use ssl and don't skip verify.\n");
ret = -1;
goto out;
}
}
option->charset = seaf_key_file_get_string (session->config,
"database", "connection_charset", NULL);
option->max_connections = g_key_file_get_integer (session->config,
"database", "max_connections",
&error);
if (error || option->max_connections < 0) {
if (error)
g_clear_error (&error);
option->max_connections = DEFAULT_MAX_CONNECTIONS;
}
load_db_option_from_env (option);
if (!option->host) {
seaf_warning ("DB host not set in config.\n");
ret = -1;
goto out;
}
if (!option->user) {
seaf_warning ("DB user not set in config.\n");
ret = -1;
goto out;
}
if (!option->passwd) {
seaf_warning ("DB passwd not set in config.\n");
ret = -1;
goto out;
}
if (!option->ccnet_db_name) {
seaf_warning ("ccnet_db_name not set in config.\n");
ret = -1;
goto out;
}
if (!option->seafile_db_name) {
seaf_warning ("db_name not set in config.\n");
ret = -1;
goto out;
}
out:
if (ret < 0) {
db_option_free (option);
return NULL;
}
return option;
}
static int
mysql_db_start (SeafileSession *session)
{
DBOption *option = NULL;
option = load_db_option (session);
if (!option) {
seaf_warning ("Failed to load database config.\n");
return -1;
}
session->db = seaf_db_new_mysql (option->host, option->port, option->user, option->passwd, option->seafile_db_name,
NULL, option->use_ssl, option->skip_verify, option->ca_path, option->charset, option->max_connections);
if (!session->db) {
db_option_free (option);
seaf_warning ("Failed to start mysql db.\n");
return -1;
}
db_option_free (option);
return 0;
}
#endif
#ifdef HAVE_POSTGRESQL
static int
pgsql_db_start (SeafileSession *session)
{
char *host, *user, *passwd, *db, *unix_socket;
unsigned int port;
GError *error = NULL;
host = seaf_key_file_get_string (session->config, "database", "host", &error);
if (!host) {
seaf_warning ("DB host not set in config.\n");
return -1;
}
user = seaf_key_file_get_string (session->config, "database", "user", &error);
if (!user) {
seaf_warning ("DB user not set in config.\n");
return -1;
}
passwd = seaf_key_file_get_string (session->config, "database", "password", &error);
if (!passwd) {
seaf_warning ("DB passwd not set in config.\n");
return -1;
}
db = seaf_key_file_get_string (session->config, "database", "db_name", &error);
if (!db) {
seaf_warning ("DB name not set in config.\n");
return -1;
}
port = g_key_file_get_integer (session->config,
"database", "port", &error);
if (error) {
port = 0;
g_clear_error (&error);
}
unix_socket = seaf_key_file_get_string (session->config,
"database", "unix_socket", &error);
session->db = seaf_db_new_pgsql (host, port, user, passwd, db, unix_socket,
DEFAULT_MAX_CONNECTIONS);
if (!session->db) {
seaf_warning ("Failed to start pgsql db.\n");
return -1;
}
g_free (host);
g_free (user);
g_free (passwd);
g_free (db);
g_free (unix_socket);
return 0;
}
#endif
int
load_database_config (SeafileSession *session)
{
char *type;
GError *error = NULL;
int ret = 0;
gboolean create_tables = FALSE;
type = seaf_key_file_get_string (session->config, "database", "type", &error);
/* Default to use sqlite if not set. */
if (!type || strcasecmp (type, "sqlite") == 0) {
ret = sqlite_db_start (session);
}
#ifdef HAVE_MYSQL
else if (strcasecmp (type, "mysql") == 0) {
ret = mysql_db_start (session);
}
#endif
#ifdef HAVE_POSTGRESQL
else if (strcasecmp (type, "pgsql") == 0) {
ret = pgsql_db_start (session);
}
#endif
else {
seaf_warning ("Unsupported db type %s.\n", type);
ret = -1;
}
if (ret == 0) {
if (g_key_file_has_key (session->config, "database", "create_tables", NULL))
create_tables = g_key_file_get_boolean (session->config,
"database", "create_tables", NULL);
session->create_tables = create_tables;
}
g_free (type);
return ret;
}
static int
ccnet_init_sqlite_database (SeafileSession *session)
{
char *db_path;
db_path = g_build_path ("/", session->ccnet_dir, CCNET_DB, NULL);
session->ccnet_db = seaf_db_new_sqlite (db_path, DEFAULT_MAX_CONNECTIONS);
if (!session->ccnet_db) {
seaf_warning ("Failed to open ccnet database.\n");
return -1;
}
return 0;
}
#ifdef HAVE_MYSQL
static int
ccnet_init_mysql_database (SeafileSession *session)
{
DBOption *option = NULL;
option = load_db_option (session);
if (!option) {
seaf_warning ("Failed to load database config.\n");
return -1;
}
session->ccnet_db = seaf_db_new_mysql (option->host, option->port, option->user, option->passwd, option->ccnet_db_name,
NULL, option->use_ssl, option->skip_verify, option->ca_path, option->charset, option->max_connections);
if (!session->ccnet_db) {
db_option_free (option);
seaf_warning ("Failed to open ccnet database.\n");
return -1;
}
db_option_free (option);
return 0;
}
#endif
int
load_ccnet_database_config (SeafileSession *session)
{
int ret;
char *engine;
gboolean create_tables = FALSE;
engine = ccnet_key_file_get_string (session->config, "database", "type");
if (!engine || strcasecmp (engine, "sqlite") == 0) {
seaf_message ("Use database sqlite\n");
ret = ccnet_init_sqlite_database (session);
}
#ifdef HAVE_MYSQL
else if (strcasecmp (engine, "mysql") == 0) {
seaf_message("Use database Mysql\n");
ret = ccnet_init_mysql_database (session);
}
#endif
#if 0
else if (strncasecmp (engine, DB_PGSQL, sizeof(DB_PGSQL)) == 0) {
ccnet_debug ("Use database PostgreSQL\n");
ret = init_pgsql_database (session);
}
#endif
else {
seaf_warning ("Unknown database type: %s.\n", engine);
ret = -1;
}
if (ret == 0) {
if (g_key_file_has_key (session->config, "database", "create_tables", NULL))
create_tables = g_key_file_get_boolean (session->config, "database", "create_tables", NULL);
session->ccnet_create_tables = create_tables;
}
g_free (engine);
return ret;
}
#ifdef FULL_FEATURE
char *
seaf_gen_notif_server_jwt (const char *repo_id, const char *username)
{
char *jwt_token = NULL;
gint64 now = (gint64)time(NULL);
jwt_t *jwt = NULL;
if (!seaf->notif_server_private_key) {
seaf_warning ("No private key is configured for generating jwt token\n");
return NULL;
}
int ret = jwt_new (&jwt);
if (ret != 0 || jwt == NULL) {
seaf_warning ("Failed to create jwt\n");
goto out;
}
ret = jwt_add_grant (jwt, "repo_id", repo_id);
if (ret != 0) {
seaf_warning ("Failed to add repo_id to jwt\n");
goto out;
}
ret = jwt_add_grant (jwt, "username", username);
if (ret != 0) {
seaf_warning ("Failed to add username to jwt\n");
goto out;
}
ret = jwt_add_grant_int (jwt, "exp", now + JWT_TOKEN_EXPIRE_TIME);
if (ret != 0) {
seaf_warning ("Failed to expire time to jwt\n");
goto out;
}
ret = jwt_set_alg (jwt, JWT_ALG_HS256, (unsigned char *)seaf->notif_server_private_key, strlen(seaf->notif_server_private_key));
if (ret != 0) {
seaf_warning ("Failed to set alg\n");
goto out;
}
jwt_token = jwt_encode_str (jwt);
out:
jwt_free (jwt);
return jwt_token;
}
#endif
char *
seaf_parse_auth_token (const char *auth_token)
{
char *token = NULL;
char **parts = NULL;
if (!auth_token) {
return NULL;
}
parts = g_strsplit (auth_token, " ", 2);
if (!parts) {
return NULL;
}
if (g_strv_length (parts) < 2) {
g_strfreev (parts);
return NULL;
}
token = g_strdup(parts[1]);
g_strfreev (parts);
return token;
}
void
split_filename (const char *filename, char **name, char **ext)
{
char *dot;
dot = strrchr (filename, '.');
if (dot) {
*ext = g_strdup (dot + 1);
*name = g_strndup (filename, dot - filename);
} else {
*name = g_strdup (filename);
*ext = NULL;
}
}