/* * uhub - A tiny ADC p2p connection hub * Copyright (C) 2007-2012, 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 . * */ #include "uhub.h" #include "util/misc.h" #include // #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; }