diff --git a/pool/page.php b/pool/page.php index d1183b4b..2f335fcf 100644 --- a/pool/page.php +++ b/pool/page.php @@ -110,8 +110,8 @@ span.err {color:red; font-weight:bold; font-size:120%;} span.alert {color:red; font-weight:bold; font-size:250%;} input.tiny {width: 0px; height: 0px; margin: 0px; padding: 0px; outline: none; border: 0px;} #n42 {margin:0; position: relative; color:#fff; background:#07e;} -#n42 a {color:#fff; text-decoration:none; margin: 4px;} -#n42 td {min-width: 100px; float: left; vertical-align: top; padding: 2px;} +#n42 a {color:#fff; text-decoration:none; padding: 6px; display:block;} +#n42 td {min-width: 100px; float: left; vertical-align: top; padding: 0px 2px;} #n42 td.navboxr {float: right;} #n42 td.nav {position: relative;} #n42 div.sub {left: 0px; z-index: 42; position: absolute; visibility: hidden;} diff --git a/pool/page_pplns.php b/pool/page_pplns.php index 1b46dd0e..cbd76be8 100644 --- a/pool/page_pplns.php +++ b/pool/page_pplns.php @@ -14,7 +14,7 @@ function stnum($num) return $b4.$fmt.$af; } # -# ... Of course ... check the output and add the txin +# ... Of course ... check the output and add the txin ... etc. function calctx($ans, $count, $miner_sat, $diffacc_total) { $pg = '
'; @@ -159,6 +159,7 @@ Block: $ans['miner_sat'] = $miner_sat; $data = array( 'Block' => 'block', + 'Block Status' => 'block_status', 'Block Hash' => 'block_hash', 'Block Reward (Satoshis)' => 'block_reward', 'Miner Reward (Satoshis)' => 'miner_sat', @@ -182,6 +183,21 @@ Block: $pg = '
Blockchain '.$ans['block']."
\n"; + + if (strlen($ans['block_extra']) > 0) + { + $pg .= '
'; + $msg = $ans['block_status'].' - '.$ans['block_extra']; + $pg .= str_replace(' ', ' ', $msg)."
\n"; + } + + if (strlen($ans['share_status']) > 0) + { + $pg .= '
'; + $msg = $ans['share_status']." - Can't be paid out yet"; + $pg .= str_replace(' ', ' ', $msg)."
\n"; + } + $pg .= "
\n"; $pg .= ''; $pg .= ''; diff --git a/pool/prime.php b/pool/prime.php index b4bd1dee..c50c69f7 100644 --- a/pool/prime.php +++ b/pool/prime.php @@ -69,8 +69,9 @@ function check() 'API' => 'api' ), 'Help' => array( - 'Help' => 'help', - 'Payouts' => 'payout' + 'Payouts' => 'payout', + 'Workers ' => 'workers', + 'Blocks' => 'blocks' ) ); tryLogInOut(); diff --git a/src/ckdb.c b/src/ckdb.c index 3554a11e..8f3357b5 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -174,8 +174,12 @@ const char *mailpatt = "^[A-Za-z0-9_-][A-Za-z0-9_\\.-]*@[A-Za-z0-9][A-Za-z0-9\\. const char *idpatt = "^[_A-Za-z][_A-Za-z0-9]*$"; const char *intpatt = "^[0-9][0-9]*$"; const char *hashpatt = "^[A-Fa-f0-9]*$"; -// TODO: bitcoind will check it properly -const char *addrpatt = "^[A-Za-z0-9]*$"; +/* BTC addresses start with '1' (one) or '3' (three), + * exclude: capital 'I' (eye), capital 'O' (oh), + * lowercase 'l' (elle) and '0' (zero) + * and with a simple test must be ADDR_MIN_LEN to ADDR_MAX_LEN (ckdb.h) + * bitcoind is used to fully validate them when required */ +const char *addrpatt = "^[13][A-HJ-NP-Za-km-z1-9]*$"; // So the records below have the same 'name' as the klist const char Transfer[] = "Transfer"; @@ -1652,7 +1656,7 @@ static void summarise_blocks() if (WMREADY(workmarkers->status)) { lookmarkersummary.markerid = workmarkers->markerid; lookmarkersummary.userid = MAXID; - lookmarkersummary.workername[0] = '\0'; + lookmarkersummary.workername = EMPTY; INIT_MARKERSUMMARY(&ms_look); ms_look.data = (void *)(&lookmarkersummary); ms_item = find_before_in_ktree(markersummary_root, &ms_look, @@ -2048,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; @@ -2151,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; @@ -2251,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__, @@ -2293,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); @@ -2499,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 dd245ef3..7767835f 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.631" +#define CKDB_VERSION DB_VERSION"-0.645" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -80,6 +80,14 @@ extern const char *intpatt; extern const char *hashpatt; extern const char *addrpatt; +/* If a trimmed username is like an address but this many or more characters, + * disallow it */ +#define ADDR_USER_CHECK 16 + +// BTC address size +#define ADDR_MIN_LEN 26 +#define ADDR_MAX_LEN 34 + typedef struct loadstatus { tv_t oldest_sharesummary_firstshare_n; tv_t newest_sharesummary_firstshare_a; @@ -295,6 +303,7 @@ enum cmd_values { CMD_DSP, CMD_STATS, CMD_PPLNS, + CMD_USERSTATUS, CMD_END }; @@ -603,7 +612,8 @@ typedef struct transfer { char *mvalue; } TRANSFER; -#define ALLOC_TRANSFER 64 +// Suggest malloc use MMAP - 1913 = largest under 2MB +#define ALLOC_TRANSFER 1913 #define LIMIT_TRANSFER 0 #define CULL_TRANSFER 1024 #define INIT_TRANSFER(_item) INIT_GENERIC(_item, transfer) @@ -632,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; @@ -1334,6 +1344,7 @@ extern PGconn *dbconnect(); extern char *safe_text(char *txt); extern void username_trim(USERS *users); +extern bool like_address(char *username); extern void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t siz, WHERE_FFL_ARGS); @@ -1534,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 c7c580c8..13761c58 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -15,7 +15,7 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, { char reply[1024] = ""; size_t siz = sizeof(reply); - K_ITEM *i_username, *i_emailaddress, *i_passwordhash, *u_item; + K_ITEM *i_username, *i_emailaddress, *i_passwordhash, *u_item = NULL; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -23,18 +23,30 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, if (!i_username) return strdup(reply); - i_emailaddress = require_name(trf_root, "emailaddress", 7, (char *)mailpatt, reply, siz); - if (!i_emailaddress) - return strdup(reply); + /* If a username added from the web site looks like an address + * then disallow it - a false positive is not an issue + * Allowing it will create a security issue - someone could create + * an account with someone else's, as yet unused, payout address + * and redirect the payout to another payout address. + * ... and the person who owns the payout address can't check that + * in advance, they'll just find out with their first payout not + * arriving at their payout address */ + if (!like_address(transfer_data(i_username))) { + i_emailaddress = require_name(trf_root, "emailaddress", 7, + (char *)mailpatt, reply, siz); + if (!i_emailaddress) + return strdup(reply); - i_passwordhash = require_name(trf_root, "passwordhash", 64, (char *)hashpatt, reply, siz); - if (!i_passwordhash) - return strdup(reply); + i_passwordhash = require_name(trf_root, "passwordhash", 64, + (char *)hashpatt, reply, siz); + if (!i_passwordhash) + return strdup(reply); - u_item = users_add(conn, transfer_data(i_username), - transfer_data(i_emailaddress), - transfer_data(i_passwordhash), - by, code, inet, now, trf_root); + u_item = users_add(conn, transfer_data(i_username), + transfer_data(i_emailaddress), + transfer_data(i_passwordhash), + by, code, inet, now, trf_root); + } if (!u_item) { LOGERR("%s() %s.failed.DBE", __func__, id); @@ -85,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; } @@ -223,7 +236,8 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, email = NULL; } i_address = optional_name(trf_root, "address", - 27, (char *)addrpatt, + ADDR_MIN_LEN, + (char *)addrpatt, reply, siz); if (i_address) address = transfer_data(i_address); @@ -248,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; @@ -2909,6 +2925,9 @@ static K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid, in (also summarising diffacc per user) then keep stepping back until we complete the current begin_workinfoid (also summarising diffacc per user) + While we are still below diff_want + find each workmarker and add on the full set of worksummary + diffacc shares (also summarising diffacc per user) This will give us the total number of diff1 shares (diffacc_total) to use for the payment calculations The value of diff_want defaults to the block's network difficulty @@ -2922,21 +2941,26 @@ static K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid, in diffacc_user * 2^32 / pplns_elapsed PPLNS fraction of the payout would be: diffacc_user / diffacc_total + + N.B. 'begin' means the oldest back in time and 'end' means the newest + 'end' should usually be the info of the found block with the pplns + data going back in time to 'begin' */ -/* TODO: redesign to include workmarkers - * ... before next payout that extends into a markersummary ... */ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, __maybe_unused tv_t *now, __maybe_unused char *by, __maybe_unused char *code, __maybe_unused char *inet, __maybe_unused tv_t *notcd, K_TREE *trf_root) { - char reply[1024], tmp[1024], *buf; + char reply[1024], tmp[1024], *buf, *block_extra, *share_status = EMPTY; size_t siz = sizeof(reply); K_ITEM *i_height, *i_difftimes, *i_diffadd, *i_allowaged; K_ITEM b_look, ss_look, *b_item, *w_item, *ss_item; + K_ITEM wm_look, *wm_item, ms_look, *ms_item; K_ITEM *mu_item, *wb_item, *u_item; SHARESUMMARY looksharesummary, *sharesummary; + WORKMARKERS lookworkmarkers, *workmarkers; + MARKERSUMMARY lookmarkersummary, *markersummary; MININGPAYOUTS *miningpayouts; WORKINFO *workinfo; TRANSFER *transfer; @@ -2945,12 +2969,12 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, K_STORE *mu_store; USERS *users; int32_t height; - int64_t workinfoid, end_workinfoid; + int64_t workinfoid, end_workinfoid = 0; int64_t begin_workinfoid; int64_t share_count; char tv_buf[DATE_BUFSIZ]; tv_t cd, begin_tv, block_tv, end_tv; - K_TREE_CTX ctx[1]; + K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1]; double ndiff, total, elapsed; double diff_times = 1.0; double diff_add = 0.0; @@ -2996,16 +3020,28 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, } DATA_BLOCKS_NULL(blocks, b_item); while (b_item && blocks->height == height) { - if (blocks->confirmed[0] == BLOCKS_CONFIRM) + // Allow any state, but report it + if (CURRENT(&(blocks->expirydate))) break; b_item = prev_in_ktree(ctx); DATA_BLOCKS_NULL(blocks, b_item); } K_RUNLOCK(blocks_free); if (!b_item || blocks->height != height) { - snprintf(reply, siz, "ERR.unconfirmed block %d", height); + snprintf(reply, siz, "ERR.no current block %d", height); return strdup(reply); } + switch (blocks->confirmed[0]) { + case BLOCKS_NEW: + block_extra = "Can't be paid out yet"; + break; + case BLOCKS_ORPHAN: + block_extra = "Can't be paid out"; + break; + default: + block_extra = EMPTY; + break; + } workinfoid = blocks->workinfoid; copy_tv(&block_tv, &(blocks->createdate)); copy_tv(&end_tv, &(blocks->createdate)); @@ -3031,31 +3067,26 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, share_count = 0; total = 0; + mu_store = k_new_store(miningpayouts_free); + mu_root = new_ktree(); + looksharesummary.workinfoid = workinfoid; looksharesummary.userid = MAXID; looksharesummary.workername[0] = '\0'; INIT_SHARESUMMARY(&ss_look); ss_look.data = (void *)(&looksharesummary); K_RLOCK(sharesummary_free); + K_RLOCK(workmarkers_free); + K_RLOCK(markersummary_free); ss_item = find_before_in_ktree(sharesummary_workinfoid_root, &ss_look, cmp_sharesummary_workinfoid, ctx); - if (!ss_item) { - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.no shares found with or before " - "workinfo %"PRId64, - workinfoid); - return strdup(reply); - } - DATA_SHARESUMMARY(sharesummary, ss_item); - - mu_store = k_new_store(miningpayouts_free); - mu_root = new_ktree(); - end_workinfoid = sharesummary->workinfoid; + DATA_SHARESUMMARY_NULL(sharesummary, ss_item); + if (ss_item) + end_workinfoid = sharesummary->workinfoid; /* add up all sharesummaries until >= diff_want * also record the latest lastshare - that will be the end pplns time * which will be >= block_tv */ - while (ss_item && total < diff_want) { + while (total < diff_want && ss_item) { switch (sharesummary->complete[0]) { case SUMMARY_CONFIRM: break; @@ -3063,14 +3094,9 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, if (allow_aged) break; default: - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.sharesummary1 not ready in " - "workinfo %"PRId64, - sharesummary->workinfoid); - goto shazbot; + share_status = "Not ready1"; } - share_count++; + share_count += sharesummary->sharecount; total += (int64_t)(sharesummary->diffacc); begin_workinfoid = sharesummary->workinfoid; if (tv_newer(&end_tv, &(sharesummary->lastshare))) @@ -3091,12 +3117,10 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, if (allow_aged) break; default: - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.sharesummary2 not ready in " - "workinfo %"PRId64, - sharesummary->workinfoid); - goto shazbot; + if (share_status == EMPTY) + share_status = "Not ready2"; + else + share_status = "Not ready1+2"; } share_count++; total += (int64_t)(sharesummary->diffacc); @@ -3106,6 +3130,50 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, ss_item = prev_in_ktree(ctx); DATA_SHARESUMMARY_NULL(sharesummary, ss_item); } + + /* If we haven't met or exceeded the required N, + * move on to the markersummaries */ + if (total < diff_want) { + lookworkmarkers.expirydate.tv_sec = default_expiry.tv_sec; + lookworkmarkers.expirydate.tv_usec = default_expiry.tv_usec; + lookworkmarkers.workinfoidend = begin_workinfoid; + INIT_WORKMARKERS(&wm_look); + wm_look.data = (void *)(&lookworkmarkers); + wm_item = find_before_in_ktree(workmarkers_root, &wm_look, + cmp_workmarkers, wm_ctx); + DATA_WORKMARKERS_NULL(workmarkers, wm_item); + while (total < diff_want && wm_item && CURRENT(&(workmarkers->expirydate))) { + if (WMREADY(workmarkers->status)) { + lookmarkersummary.markerid = workmarkers->markerid; + lookmarkersummary.userid = MAXID; + lookmarkersummary.workername = EMPTY; + INIT_MARKERSUMMARY(&ms_look); + ms_look.data = (void *)(&lookmarkersummary); + ms_item = find_before_in_ktree(markersummary_root, &ms_look, + cmp_markersummary, ms_ctx); + DATA_MARKERSUMMARY_NULL(markersummary, ms_item); + // add the whole markerid + while (ms_item && markersummary->markerid == workmarkers->markerid) { + if (end_workinfoid == 0) + end_workinfoid = workmarkers->workinfoidend; + share_count += markersummary->sharecount; + total += (int64_t)(markersummary->diffacc); + begin_workinfoid = workmarkers->workinfoidstart; + if (tv_newer(&end_tv, &(markersummary->lastshare))) + copy_tv(&end_tv, &(markersummary->lastshare)); + mu_root = upd_add_mu(mu_root, mu_store, + markersummary->userid, + (int64_t)(markersummary->diffacc)); + ms_item = prev_in_ktree(ms_ctx); + DATA_MARKERSUMMARY_NULL(markersummary, ms_item); + } + } + wm_item = prev_in_ktree(wm_ctx); + DATA_WORKMARKERS_NULL(workmarkers, wm_item); + } + } + K_RUNLOCK(markersummary_free); + K_RUNLOCK(workmarkers_free); K_RUNLOCK(sharesummary_free); if (total == 0.0) { @@ -3140,6 +3208,13 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "block_reward=%"PRId64"%c", blocks->reward, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "block_status=%s%c", + blocks_confirmed(blocks->confirmed), FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "block_extra=%s%c", block_extra, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "share_status=%s%c", share_status, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "workername=%s%c", blocks->workername, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "nonce=%s%c", blocks->nonce, FLDSEP); @@ -3379,6 +3454,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" @@ -3479,7 +3617,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_data.c b/src/ckdb_data.c index e712f520..b799648d 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -67,6 +67,55 @@ void username_trim(USERS *users) } } +/* Is the trimmed username like an address? + * False positive is OK (i.e. 'like') + * Before checking, it is trimmed to avoid web display confusion + * Length check is done before trimming - this may give a false + * positive on any username with lots of trim characters ... which is OK */ +bool like_address(char *username) +{ + char *tmp, *front, *trail; + size_t len; + regex_t re; + int ret; + + len = strlen(username); + if (len < ADDR_USER_CHECK) + return false; + + tmp = strdup(username); + front = tmp; + while (*front && TRIM_IGNORE(*front)) + front++; + + trail = front + strlen(front) - 1; + while (trail >= front) { + if (TRIM_IGNORE(*trail)) + *(trail--) = '\0'; + else + break; + } + + if (regcomp(&re, addrpatt, REG_NOSUB) != 0) { + LOGEMERG("%s(): failed to compile addrpatt '%s'", + __func__, addrpatt); + free(tmp); + // This will disable adding any new usernames ... + return true; + } + + ret = regexec(&re, front, (size_t)0, NULL, 0); + regfree(&re); + + if (ret == 0) { + free(tmp); + return true; + } + + free(tmp); + return false; +} + void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t siz, WHERE_FFL_ARGS) { char *tmp; diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 8c0b3d52..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); @@ -4055,6 +4061,10 @@ bool auths_add(PGconn *conn, char *poolinstance, char *username, } DATA_USERS(*users, u_item); + // Any status content means disallow mining + if ((*users)->status[0]) + goto unitem; + STRNCPY(row->poolinstance, poolinstance); row->userid = (*users)->userid; // since update=false, a dup will be ok and do nothing when igndup=true
Name