diff --git a/src/ckdb.c b/src/ckdb.c index c43ae064..11a0274f 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2052,6 +2052,7 @@ static void *socketer(__maybe_unused void *arg) char *last_newid = NULL, *reply_newid = NULL; char *last_setatts = NULL, *reply_setatts = NULL; char *last_setopts = NULL, *reply_setopts = NULL; + char *last_userstatus = NULL, *reply_userstatus = NULL; char *last_web = NULL, *reply_web = NULL; char *reply_last, duptype[CMD_SIZ+1]; enum cmd_values cmdnum; @@ -2155,6 +2156,9 @@ static void *socketer(__maybe_unused void *arg) } else if (last_setopts && strcmp(last_setopts, buf) == 0) { reply_last = reply_setopts; dup = true; + } else if (last_userstatus && strcmp(last_userstatus, buf) == 0) { + reply_last = reply_userstatus; + dup = true; } else if (last_web && strcmp(last_web, buf) == 0) { reply_last = reply_web; dup = true; @@ -2255,6 +2259,7 @@ static void *socketer(__maybe_unused void *arg) case CMD_BLOCKLIST: case CMD_NEWID: case CMD_STATS: + case CMD_USERSTATUS: ans = ckdb_cmds[which_cmds].func(NULL, cmd, id, &now, by_default, (char *)__func__, @@ -2297,6 +2302,9 @@ static void *socketer(__maybe_unused void *arg) case CMD_SETOPTS: STORELASTREPLY(setopts); break; + case CMD_USERSTATUS: + STORELASTREPLY(userstatus); + break; // The rest default: free(rep); @@ -2503,6 +2511,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) case CMD_DSP: case CMD_STATS: case CMD_PPLNS: + case CMD_USERSTATUS: LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored", __func__, count, cmd); break; diff --git a/src/ckdb.h b/src/ckdb.h index bb35f06d..e7341af0 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.4" -#define CKDB_VERSION DB_VERSION"-0.642" +#define CKDB_VERSION DB_VERSION"-0.643" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -303,6 +303,7 @@ enum cmd_values { CMD_DSP, CMD_STATS, CMD_PPLNS, + CMD_USERSTATUS, CMD_END }; @@ -641,7 +642,7 @@ typedef struct users { int64_t userid; char username[TXT_BIG+1]; char usertrim[TXT_BIG+1]; // Non DB field - // TODO: Anything in 'status' disables the account + // Anything in 'status' fails mining authentication char status[TXT_BIG+1]; char emailaddress[TXT_BIG+1]; tv_t joineddate; @@ -1544,9 +1545,9 @@ extern char *pqerrmsg(PGconn *conn); extern int64_t nextid(PGconn *conn, char *idname, int64_t increment, tv_t *cd, char *by, char *code, char *inet); -extern bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, - char *newhash, char *email, char *by, char *code, - char *inet, tv_t *cd, K_TREE *trf_root); +extern bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash, + char *newhash, char *email, char *by, char *code, + char *inet, tv_t *cd, K_TREE *trf_root, char *status); extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress, char *passwordhash, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root); diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index e3443858..53d9cbef 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -97,12 +97,13 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id, K_RUNLOCK(users_free); if (u_item) { - ok = users_pass_email(NULL, u_item, - oldhash, - transfer_data(i_newhash), - NULL, - by, code, inet, now, - trf_root); + ok = users_update(NULL, u_item, + oldhash, + transfer_data(i_newhash), + NULL, + by, code, inet, now, + trf_root, + NULL); } else ok = false; } @@ -261,10 +262,12 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } if (email && *email) { - ok = users_pass_email(conn, u_item, NULL, - NULL, email, - by, code, inet, - now, trf_root); + ok = users_update(conn, u_item, + NULL, NULL, + email, + by, code, inet, now, + trf_root, + NULL); if (!ok) { reason = "email error"; goto struckout; @@ -3392,6 +3395,69 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id, return buf; } +// TODO: add to heartbeat to disable the miner if active and status != "" +static char *cmd_userstatus(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, + char *code, char *inet, __maybe_unused tv_t *cd, + K_TREE *trf_root) +{ + char reply[1024] = ""; + size_t siz = sizeof(reply); + K_ITEM *i_username, *i_userid, *i_status, *u_item; + int64_t userid; + char *status; + USERS *users; + bool ok; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + i_username = optional_name(trf_root, "username", 3, (char *)userpatt, reply, siz); + i_userid = optional_name(trf_root, "userid", 1, (char *)intpatt, reply, siz); + // Either username or userid + if (!i_username && !i_userid) { + snprintf(reply, siz, "failed.invalid/missing userinfo"); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + + // A zero length status re-enables it + i_status = require_name(trf_root, "status", 0, NULL, reply, siz); + if (!i_status) + return strdup(reply); + status = transfer_data(i_status); + + K_RLOCK(users_free); + if (i_username) + u_item = find_users(transfer_data(i_username)); + else { + TXT_TO_BIGINT("userid", transfer_data(i_userid), userid); + u_item = find_userid(userid); + } + K_RUNLOCK(users_free); + + if (!u_item) + ok = false; + else { + ok = users_update(conn, u_item, + NULL, NULL, + NULL, + by, code, inet, now, + trf_root, + status); + } + + if (!ok) { + LOGERR("%s() %s.failed.DBE", __func__, id); + return strdup("failed.DBE"); + } + DATA_USERS(users, u_item); + snprintf(reply, siz, "ok.updated %"PRId64" %s status %s", + users->userid, + users->username, + status[0] ? "disabled" : "enabled"); + LOGWARNING("%s.%s", id, reply); + return strdup(reply); +} + // TODO: limit access by having seperate sockets for each #define ACCESS_POOL "p" #define ACCESS_SYSTEM "s" @@ -3492,7 +3558,8 @@ struct CMDS ckdb_cmds[] = { { CMD_GETOPTS, "getopts", false, false, cmd_getopts, ACCESS_WEB }, { CMD_SETOPTS, "setopts", false, false, cmd_setopts, ACCESS_WEB }, { CMD_DSP, "dsp", false, false, cmd_dsp, ACCESS_SYSTEM }, - { CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM }, - { CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM }, + { CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM ACCESS_WEB }, + { CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM ACCESS_WEB }, + { CMD_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB }, { CMD_END, NULL, false, false, NULL, NULL } }; diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index f837e78b..c8d1d864 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -304,9 +304,10 @@ cleanup: return lastid; } -bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, - char *newhash, char *email, char *by, char *code, - char *inet, tv_t *cd, K_TREE *trf_root) +// status was added to the end so type checking intercepts new mistakes +bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash, + char *newhash, char *email, char *by, char *code, + char *inet, tv_t *cd, K_TREE *trf_root, char *status) { ExecStatusType rescode; bool conned = false; @@ -315,7 +316,7 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, USERS *row, *users; char *upd, *ins; bool ok = false; - char *params[5 + HISTORYDATECOUNT]; + char *params[6 + HISTORYDATECOUNT]; bool hash; int n, par = 0; @@ -337,14 +338,18 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, DATA_USERS(row, item); memcpy(row, users, sizeof(*row)); - // Update one, leave the other + + // Update each one supplied if (hash) { // New salt each password change make_salt(row); password_hash(row->username, newhash, row->salt, row->passwordhash, sizeof(row->passwordhash)); - } else + } + if (email) STRNCPY(row->emailaddress, email); + if (status) + STRNCPY(row->status, status); HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATETRANSFER(trf_root, row); @@ -384,21 +389,22 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, par = 0; params[par++] = bigint_to_buf(row->userid, NULL, 0); params[par++] = tv_to_buf(cd, NULL, 0); - // Copy them both in - one will be new and one will be old + // Copy them all in - at least one will be new + params[par++] = str_to_buf(row->status, NULL, 0); params[par++] = str_to_buf(row->emailaddress, NULL, 0); params[par++] = str_to_buf(row->passwordhash, NULL, 0); // New salt for each password change (or recopy old) params[par++] = str_to_buf(row->salt, NULL, 0); HISTORYDATEPARAMS(params, par, row); - PARCHKVAL(par, 5 + HISTORYDATECOUNT, params); // 10 as per ins + PARCHKVAL(par, 6 + HISTORYDATECOUNT, params); // 11 as per ins ins = "insert into users " "(userid,username,status,emailaddress,joineddate," "passwordhash,secondaryuserid,salt" HISTORYDATECONTROL ") select " - "userid,username,status,$3,joineddate," - "$4,secondaryuserid,$5," - "$6,$7,$8,$9,$10 from users where " + "userid,username,$3,$4,joineddate," + "$5,secondaryuserid,$6," + "$7,$8,$9,$10,$11 from users where " "userid=$1 and expirydate=$2"; res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);