diff --git a/mxqset.c b/mxqset.c new file mode 100644 index 00000000..18cf375a --- /dev/null +++ b/mxqset.c @@ -0,0 +1,245 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include "mx_mysql.h" +#include "mxq.h" +#include "mxq_group.h" +#include "keywordset.h" +#include "stdarg.h" + +static __attribute__ ((noreturn)) void die(char *msg, ...) { + va_list ap; + + va_start(ap, msg); + fprintf(stderr, "%s: ",program_invocation_short_name); + vfprintf(stderr, msg, ap); + va_end(ap); + exit(1); +} + +static void verify_group_permission(struct mx_mysql *mysql , long unsigned groupid, uid_t client_uid) { + struct mx_mysql_stmt *stmt = NULL; + unsigned long long num_rows; + unsigned uid; + + stmt = mx_mysql_statement_prepare(mysql,"SELECT user_uid FROM mxq_group WHERE group_id = ? LIMIT 1"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, uint64, &(groupid)); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_result_bind(stmt, 0, uint32, &uid); + if (mx_mysql_statement_fetch(stmt) < 0) + die("%m\n"); + if ( client_uid != 0 && client_uid != uid ) + die("no permission to access this group\n"); + mx_mysql_statement_close(&stmt); +} + +enum OPEN_CLOSED { + OPEN_CLOSED_UNSET = 0, + OPEN_CLOSED_CLOSED, + OPEN_CLOSED_OPEN, +}; + +static void update_closed_flag(struct mx_mysql *mysql, long unsigned groupid, enum OPEN_CLOSED open_closed) { + + struct mx_mysql_stmt *stmt = NULL; + unsigned long long num_rows; + + uint64_t mask = ~MXQ_GROUP_FLAG_CLOSED; + uint64_t value = open_closed == OPEN_CLOSED_CLOSED ? MXQ_GROUP_FLAG_CLOSED : 0; + + stmt = mx_mysql_statement_prepare(mysql, + "UPDATE mxq_group SET group_flags = ( group_flags & ?) | ? WHERE group_id = ?"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, uint64, &(mask)); + mx_mysql_statement_param_bind(stmt, 1, uint64, &(value)); + mx_mysql_statement_param_bind(stmt, 2, uint64, &(groupid)); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_close(&stmt); +} + +static void update_blacklist(struct mx_mysql *mysql, long unsigned groupid, char *update_blacklist) { + + struct mx_mysql_stmt *stmt = NULL; + unsigned long long num_rows; + char *group_blacklist = NULL; + struct keywordset *blacklist; + + if (strcmp(update_blacklist, "") !=0 ) { + stmt = mx_mysql_statement_prepare(mysql, + "SELECT group_blacklist FROM mxq_group WHERE group_id = ? LIMIT 1"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, uint64, &groupid); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_result_bind(stmt, 0, string, &group_blacklist); + if (mx_mysql_statement_fetch(stmt) < 0) + die("%m\n"); + mx_mysql_statement_close(&stmt); + blacklist = keywordset_new(group_blacklist); + free(group_blacklist); + } else { + blacklist = keywordset_new(NULL); + } + keywordset_update(blacklist, update_blacklist); + + group_blacklist = keywordset_get(blacklist); + + stmt = mx_mysql_statement_prepare(mysql, + "UPDATE mxq_group SET group_blacklist = ? WHERE group_id = ?"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, string, &group_blacklist); + mx_mysql_statement_param_bind(stmt, 1, uint64, &groupid); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_close(&stmt); + free(group_blacklist); + keywordset_free(blacklist); +} + +static void update_whitelist(struct mx_mysql *mysql, long unsigned groupid, char *update_whitelist) { + + struct mx_mysql_stmt *stmt = NULL; + unsigned long long num_rows; + char *group_whitelist = NULL; + struct keywordset *whitelist; + + if (strcmp(update_whitelist, "") !=0 ) { + stmt = mx_mysql_statement_prepare(mysql, + "SELECT group_whitelist FROM mxq_group WHERE group_id = ? LIMIT 1"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, uint64, &groupid); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_result_bind(stmt, 0, string, &group_whitelist); + if (mx_mysql_statement_fetch(stmt) < 0) + die("%m\n"); + mx_mysql_statement_close(&stmt); + whitelist = keywordset_new(group_whitelist); + free(group_whitelist); + } else { + whitelist = keywordset_new(NULL); + } + keywordset_update(whitelist, update_whitelist); + + group_whitelist = keywordset_get(whitelist); + + stmt = mx_mysql_statement_prepare(mysql, + "UPDATE mxq_group SET group_whitelist = ? WHERE group_id = ?"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, string, &group_whitelist); + mx_mysql_statement_param_bind(stmt, 1, uint64, &groupid); + if (mx_mysql_statement_execute(stmt, &num_rows) < 0) + die("%m\n"); + if (num_rows < 1) + die("no such group %ld\n", groupid); + mx_mysql_statement_close(&stmt); + free(group_whitelist); + keywordset_free(whitelist); +} + +struct opts { + enum OPEN_CLOSED open_closed; + char *update_blacklist; + char *update_whitelist; +}; + +static error_t parser (int key, char *arg, struct argp_state *state) { + struct opts *opts = state->input; + switch (key) { + case 10: + opts->open_closed = OPEN_CLOSED_CLOSED; + return 0; + case 11: + opts->open_closed = OPEN_CLOSED_OPEN; + return 0; + case 13: + opts->update_blacklist = arg; + return 0; + case 15: + opts->update_whitelist = arg; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static const struct argp_option options[] = { + {"closed", 10, NULL, 0, NULL}, + {"open", 11, NULL, 0, NULL}, + {"blacklist", 13, "", 0, NULL}, + {"whitelist", 15, "", 0, NULL}, + {0} +}; + +static const struct argp argp = { options, parser, NULL, NULL }; + +static __attribute__ ((noreturn)) void exit_usage(char *argv0) { + fprintf(stderr, +"usage: %s group GID [group-options]\n" +"\n" +"group options:\n" +"\n" +" --open\n" +" --closed\n" +" --blacklist=STRING\n" +" --whitelist=STRING\n" + ,program_invocation_short_name); + exit(EX_USAGE); +} + +int main(int argc, char **argv) { + + char *argv0=argv[0]; + struct mx_mysql *mysql = NULL; + int groupid; + uid_t uid = getuid(); + + if (argc<3 || strcmp(argv[1],"group") != 0) + exit_usage(argv0); + groupid=atoi(argv[2]); + + int sts; + struct opts opts={0}; + + sts=argp_parse (&argp, argc-3, &argv[3], ARGP_PARSE_ARGV0|ARGP_SILENT, NULL, &opts); + if (sts) + exit_usage(argv0); + + assert(mx_mysql_initialize(&mysql) == 0); + mx_mysql_option_set_default_file(mysql, MXQ_MYSQL_DEFAULT_FILE); + mx_mysql_option_set_default_group(mysql, MXQ_MYSQL_DEFAULT_GROUP); + assert(mx_mysql_connect_forever(&mysql) == 0); + + verify_group_permission(mysql, groupid, uid); + + if ( opts.open_closed != OPEN_CLOSED_UNSET) + update_closed_flag(mysql, groupid, opts.open_closed); + if ( opts.update_blacklist != NULL) + update_blacklist(mysql, groupid, opts.update_blacklist); + if ( opts.update_whitelist != NULL) + update_whitelist(mysql, groupid, opts.update_whitelist); + + mx_mysql_finish(&mysql); +}