396 lines
8.1 KiB
C
396 lines
8.1 KiB
C
/*
|
|
* uhub - A tiny ADC p2p connection hub
|
|
* Copyright (C) 2007-2013, Jan Vidar Krey
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "uhub.h"
|
|
#include "util/misc.h"
|
|
#include <sqlite3.h>
|
|
|
|
// #define DEBUG_SQL
|
|
|
|
static sqlite3* db = NULL;
|
|
static const char* command = NULL;
|
|
static const char* filename = NULL;
|
|
static const char* binary = NULL;
|
|
|
|
typedef int (*command_func_t)(size_t, const char**);
|
|
|
|
static int create(size_t argc, const char** argv);
|
|
static int list(size_t argc, const char** argv);
|
|
static int pass(size_t argc, const char** argv);
|
|
static int add(size_t argc, const char** argv);
|
|
static int del(size_t argc, const char** argv);
|
|
static int mod(size_t argc, const char** argv);
|
|
|
|
static struct commands
|
|
{
|
|
command_func_t handle;
|
|
const char* command;
|
|
const char* usage;
|
|
} COMMANDS[6] = {
|
|
{ &create, "create", "" },
|
|
{ &list, "list", "" },
|
|
{ &add, "add", "username password [credentials = user]" },
|
|
{ &del, "del", "username" },
|
|
{ &mod, "mod", "username credentials" },
|
|
{ &pass, "pass", "username password" },
|
|
};
|
|
|
|
static void print_usage(const char* str)
|
|
{
|
|
fprintf(stderr, "Usage: %s filename %s %s\n", binary, command, str);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/**
|
|
* Escape an SQL statement and return a pointer to the string.
|
|
* NOTE: The returned value needs to be free'd.
|
|
*
|
|
* @return an escaped string.
|
|
*/
|
|
static char* sql_escape_string(const char* str)
|
|
{
|
|
size_t i, n, size;
|
|
char* buf;
|
|
|
|
for (n = 0, size = strlen(str); n < strlen(str); n++)
|
|
if (str[n] == '\'')
|
|
size++;
|
|
|
|
buf = malloc(size+1);
|
|
for (n = 0, i = 0; n < strlen(str); n++)
|
|
{
|
|
if (str[n] == '\'')
|
|
buf[i++] = '\'';
|
|
buf[i++] = str[n];
|
|
}
|
|
buf[i++] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Validate credentials.
|
|
*/
|
|
static const char* validate_cred(const char* cred_str)
|
|
{
|
|
if (!strcmp(cred_str, "admin"))
|
|
return "admin";
|
|
|
|
if (!strcmp(cred_str, "super"))
|
|
return "super";
|
|
|
|
if (!strcmp(cred_str, "op"))
|
|
return "op";
|
|
|
|
if (!strcmp(cred_str, "user"))
|
|
return "user";
|
|
|
|
fprintf(stderr, "Invalid user credentials. Must be one of: 'admin', 'super', 'op' or 'user'\n");
|
|
exit(1);
|
|
}
|
|
|
|
static const char* validate_username(const char* username)
|
|
{
|
|
const char* tmp;
|
|
|
|
// verify length
|
|
if (strlen(username) > MAX_NICK_LEN)
|
|
{
|
|
fprintf(stderr, "User name is too long.\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Nick must not start with a space */
|
|
if (is_white_space(username[0]))
|
|
{
|
|
fprintf(stderr, "User name cannot start with white space.\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Check for ASCII values below 32 */
|
|
for (tmp = username; *tmp; tmp++)
|
|
if ((*tmp < 32) && (*tmp > 0))
|
|
{
|
|
fprintf(stderr, "User name contains illegal characters.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (!is_valid_utf8(username))
|
|
{
|
|
fprintf(stderr, "User name must be utf-8 encoded.\n");
|
|
exit(1);
|
|
}
|
|
|
|
return username;
|
|
}
|
|
|
|
|
|
static const char* validate_password(const char* password)
|
|
{
|
|
// verify length
|
|
if (strlen(password) > MAX_PASS_LEN)
|
|
{
|
|
fprintf(stderr, "Password is too long.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (!is_valid_utf8(password))
|
|
{
|
|
fprintf(stderr, "Password must be utf-8 encoded.\n");
|
|
exit(1);
|
|
}
|
|
|
|
return password;
|
|
}
|
|
|
|
static void open_database()
|
|
{
|
|
int res = sqlite3_open(filename, &db);
|
|
|
|
if (res)
|
|
{
|
|
fprintf(stderr, "Unable to open database: %s (result=%d)\n", filename, res);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int sql_callback(void* ptr, int argc, char **argv, char **colName) { return 0; }
|
|
|
|
static int sql_execute(const char* sql, ...)
|
|
{
|
|
va_list args;
|
|
char query[1024];
|
|
char* errMsg;
|
|
int rc;
|
|
|
|
va_start(args, sql);
|
|
vsnprintf(query, sizeof(query), sql, args);
|
|
|
|
#ifdef DEBUG_SQL
|
|
printf("SQL: %s\n", query);
|
|
#endif
|
|
|
|
open_database();
|
|
|
|
rc = sqlite3_exec(db, query, sql_callback, NULL, &errMsg);
|
|
if (rc != SQLITE_OK) {
|
|
fprintf(stderr, "ERROR: %s\n", errMsg);
|
|
sqlite3_free(errMsg);
|
|
}
|
|
|
|
rc = sqlite3_changes(db);
|
|
sqlite3_close(db);
|
|
return rc;
|
|
}
|
|
|
|
static int create(size_t argc, const char** argv)
|
|
{
|
|
const char* sql = "CREATE TABLE users"
|
|
"("
|
|
"nickname CHAR NOT NULL UNIQUE,"
|
|
"password CHAR NOT NULL,"
|
|
"credentials CHAR NOT NULL DEFAULT 'user',"
|
|
"created TIMESTAMP DEFAULT (DATETIME('NOW')),"
|
|
"activity TIMESTAMP DEFAULT (DATETIME('NOW'))"
|
|
");";
|
|
|
|
sql_execute(sql);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int sql_callback_list(void* ptr, int argc, char **argv, char **colName)
|
|
{
|
|
int* found = (int*) ptr;
|
|
uhub_assert(strcmp(colName[0], "nickname") == 0 && strcmp(colName[2], "credentials") == 0);
|
|
printf("%s\t%s\n", argv[2], argv[0]);
|
|
(*found)++;
|
|
return 0;
|
|
}
|
|
|
|
static int list(size_t argc, const char** argv)
|
|
{
|
|
char* errMsg;
|
|
int found = 0;
|
|
int rc;
|
|
|
|
open_database();
|
|
|
|
rc = sqlite3_exec(db, "SELECT * FROM users;", sql_callback_list, &found, &errMsg);
|
|
if (rc != SQLITE_OK) {
|
|
#ifdef DEBUG_SQL
|
|
fprintf(stderr, "SQL: ERROR: %s (%d)\n", errMsg, rc);
|
|
#endif
|
|
sqlite3_free(errMsg);
|
|
exit(1);
|
|
}
|
|
|
|
sqlite3_close(db);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int add(size_t argc, const char** argv)
|
|
{
|
|
char* user = NULL;
|
|
char* pass = NULL;
|
|
const char* cred = NULL;
|
|
int rc;
|
|
|
|
if (argc < 2)
|
|
print_usage("username password [credentials = user]");
|
|
|
|
user = sql_escape_string(validate_username(argv[0]));
|
|
pass = sql_escape_string(validate_password(argv[1]));
|
|
cred = validate_cred(argv[2] ? argv[2] : "user");
|
|
|
|
rc = sql_execute("INSERT INTO users (nickname, password, credentials) VALUES('%s', '%s', '%s');", user, pass, cred);
|
|
|
|
free(user);
|
|
free(pass);
|
|
|
|
if (rc != 1)
|
|
{
|
|
fprintf(stderr, "Unable to add user \"%s\"\n", argv[0]);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mod(size_t argc, const char** argv)
|
|
{
|
|
char* user = NULL;
|
|
const char* cred = NULL;
|
|
int rc;
|
|
|
|
if (argc < 2)
|
|
print_usage("username credentials");
|
|
|
|
user = sql_escape_string(argv[0]);
|
|
cred = validate_cred(argv[1]);
|
|
|
|
rc = sql_execute("UPDATE users SET credentials = '%s' WHERE nickname = '%s';", cred, user);
|
|
|
|
free(user);
|
|
|
|
if (rc != 1)
|
|
{
|
|
fprintf(stderr, "Unable to set credentials for user \"%s\"\n", argv[0]);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int pass(size_t argc, const char** argv)
|
|
{
|
|
char* user = NULL;
|
|
char* pass = NULL;
|
|
int rc;
|
|
|
|
if (argc < 2)
|
|
print_usage("username password");
|
|
|
|
user = sql_escape_string(argv[0]);
|
|
pass = sql_escape_string(validate_password(argv[1]));
|
|
|
|
rc = sql_execute("UPDATE users SET password = '%s' WHERE nickname = '%s';", pass, user);
|
|
|
|
free(user);
|
|
free(pass);
|
|
|
|
if (rc != 1)
|
|
{
|
|
fprintf(stderr, "Unable to change password for user \"%s\"\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int del(size_t argc, const char** argv)
|
|
{
|
|
char* user = NULL;
|
|
int rc;
|
|
|
|
if (argc < 1)
|
|
print_usage("username");
|
|
|
|
user = sql_escape_string(argv[0]);
|
|
|
|
rc = sql_execute("DELETE FROM users WHERE nickname = '%s';", user);
|
|
free(user);
|
|
|
|
if (rc != 1)
|
|
{
|
|
fprintf(stderr, "Unable to delete user \"%s\".\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void main_usage(const char* binary)
|
|
{
|
|
printf(
|
|
"Usage: %s filename command [...]\n"
|
|
"\n"
|
|
"Command syntax:\n"
|
|
" create\n"
|
|
" add username password [credentials = user]\n"
|
|
" del username\n"
|
|
" mod username credentials\n"
|
|
" pass username password\n"
|
|
" list\n"
|
|
"\n"
|
|
"Parameters:\n"
|
|
" 'filename' is a database file\n"
|
|
" 'username' is a nickname (UTF-8, up to %i bytes)\n"
|
|
" 'password' is a password (UTF-8, up to %i bytes)\n"
|
|
" 'credentials' is one of 'admin', 'super', 'op', 'user'\n"
|
|
"\n"
|
|
, binary, MAX_NICK_LEN, MAX_PASS_LEN);
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
size_t n = 0;
|
|
binary = argv[0];
|
|
filename = argv[1];
|
|
command = argv[2];
|
|
|
|
if (argc < 3)
|
|
{
|
|
main_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
for (; n < sizeof(COMMANDS) / sizeof(COMMANDS[0]); n++)
|
|
{
|
|
if (!strcmp(command, COMMANDS[n].command))
|
|
return COMMANDS[n].handle(argc - 3, (const char**) &argv[3]);
|
|
}
|
|
|
|
// Unknown command!
|
|
main_usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
|