diff --git a/mxqset.c b/mxqset.c new file mode 100644 index 00000000..891e9439 --- /dev/null +++ b/mxqset.c @@ -0,0 +1,200 @@ +#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_disabled_server(struct mx_mysql *mysql, long unsigned groupid, char *set_disabled_server, char *update_disabled_server) { + + struct mx_mysql_stmt *stmt = NULL; + unsigned long long num_rows; + char *group_disabled_servers = NULL; + struct keywordset *disabled_server; + + if (set_disabled_server) + disabled_server = keywordset_new(set_disabled_server); + else { + stmt = mx_mysql_statement_prepare(mysql, + "SELECT group_disabled_servers 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_disabled_servers); + if (mx_mysql_statement_fetch(stmt) < 0) + die("%m\n"); + mx_mysql_statement_close(&stmt); + disabled_server = keywordset_new(group_disabled_servers); + free(group_disabled_servers); + } + + if (update_disabled_server != NULL) + keywordset_update(disabled_server, update_disabled_server); + + group_disabled_servers = keywordset_get(disabled_server); + + stmt = mx_mysql_statement_prepare(mysql, + "UPDATE mxq_group SET group_disabled_servers = ? WHERE group_id = ?"); + if (!stmt) + die("%m\n"); + mx_mysql_statement_param_bind(stmt, 0, string, &group_disabled_servers); + 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_disabled_servers); + keywordset_free(disabled_server); +} + +struct opts { + enum OPEN_CLOSED open_closed; + char *set_disabled_server; + char *update_disabled_server; +}; + +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 12: + opts->set_disabled_server = arg; + return 0; + case 13: + opts->update_disabled_server = arg; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static const struct argp_option options[] = { + {"closed", 10, NULL, 0, NULL}, + {"open", 11, NULL, 0, NULL}, + {"disabled-servers", 12, "", 0, NULL}, + {"update-disabled-servers", 13, "", 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" +" --disabled-servers=STRING\n" +" --update-disabled-servers=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.set_disabled_server != NULL || opts.update_disabled_server != NULL) + update_disabled_server(mysql, groupid, opts.set_disabled_server, opts.update_disabled_server); + + mx_mysql_finish(&mysql); +}