|
|
|
@ -49,7 +49,7 @@
|
|
|
|
|
|
|
|
|
|
#define DB_VLOCK "1" |
|
|
|
|
#define DB_VERSION "0.9.2" |
|
|
|
|
#define CKDB_VERSION DB_VERSION"-0.300" |
|
|
|
|
#define CKDB_VERSION DB_VERSION"-0.302" |
|
|
|
|
|
|
|
|
|
#define WHERE_FFL " - from %s %s() line %d" |
|
|
|
|
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ |
|
|
|
@ -800,6 +800,8 @@ enum cmd_values {
|
|
|
|
|
CMD_WORKERS, |
|
|
|
|
CMD_ALLUSERS, |
|
|
|
|
CMD_HOMEPAGE, |
|
|
|
|
CMD_GETATTS, |
|
|
|
|
CMD_SETATTS, |
|
|
|
|
CMD_DSP, |
|
|
|
|
CMD_STATS, |
|
|
|
|
CMD_PPLNS, |
|
|
|
@ -910,8 +912,8 @@ static char *_transfer_data(K_ITEM *item, WHERE_FFL_ARGS)
|
|
|
|
|
} |
|
|
|
|
mvalue = transfer->mvalue; |
|
|
|
|
if (!mvalue) { |
|
|
|
|
/* N.B. name and svalue will terminate even if they are corrupt
|
|
|
|
|
* since mvalue is NULL */ |
|
|
|
|
/* N.B. name and svalue strings will have \0 termination
|
|
|
|
|
* even if they are both corrupt, since mvalue is NULL */ |
|
|
|
|
quitfrom(1, file, func, line, |
|
|
|
|
"Transfer '%s' '%s' has NULL mvalue", |
|
|
|
|
transfer->name, transfer->svalue); |
|
|
|
@ -973,17 +975,15 @@ typedef struct users {
|
|
|
|
|
#define SALTSIZHEX 32 |
|
|
|
|
#define SALTSIZBIN 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static K_TREE *users_root; |
|
|
|
|
static K_TREE *userid_root; |
|
|
|
|
static K_LIST *users_free; |
|
|
|
|
static K_STORE *users_store; |
|
|
|
|
|
|
|
|
|
/* TODO:
|
|
|
|
|
// USERATTS
|
|
|
|
|
typedef struct useratts { |
|
|
|
|
int64_t userid; |
|
|
|
|
char attname[TXT_BIG+1]; |
|
|
|
|
char attname[TXT_SML+1]; |
|
|
|
|
char status[TXT_BIG+1]; |
|
|
|
|
char attstr[TXT_BIG+1]; |
|
|
|
|
char attstr2[TXT_BIG+1]; |
|
|
|
@ -998,12 +998,11 @@ typedef struct useratts {
|
|
|
|
|
#define LIMIT_USERATTS 0 |
|
|
|
|
#define INIT_USERATTS(_item) INIT_GENERIC(_item, useratts) |
|
|
|
|
#define DATA_USERATTS(_var, _item) DATA_GENERIC(_var, _item, useratts, true) |
|
|
|
|
#define DATA_USERATTS_NULL(_var, _item) DATA_GENERIC(_var, _item, useratts, true) |
|
|
|
|
#define DATA_USERATTS_NULL(_var, _item) DATA_GENERIC(_var, _item, useratts, false) |
|
|
|
|
|
|
|
|
|
static K_TREE *useratts_root; |
|
|
|
|
static K_LIST *useratts_free; |
|
|
|
|
static K_STORE *useratts_store; |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
// WORKERS
|
|
|
|
|
typedef struct workers { |
|
|
|
@ -1401,6 +1400,7 @@ static K_STORE *auths_store;
|
|
|
|
|
|
|
|
|
|
// POOLSTATS poolstats.id.json={...}
|
|
|
|
|
// Store every > 9.5m?
|
|
|
|
|
// TODO: redo like userstats, but every 10min
|
|
|
|
|
#define STATS_PER (9.5*60.0) |
|
|
|
|
|
|
|
|
|
typedef struct poolstats { |
|
|
|
@ -2664,6 +2664,7 @@ static K_ITEM *find_userid(int64_t userid)
|
|
|
|
|
return find_in_ktree(userid_root, &look, cmp_userid, ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: endian?
|
|
|
|
|
static void make_salt(USERS *users) |
|
|
|
|
{ |
|
|
|
|
long int r1, r2, r3, r4; |
|
|
|
@ -3119,6 +3120,357 @@ void users_reload()
|
|
|
|
|
PQfinish(conn); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// default tree order by userid asc,attname asc,expirydate desc
|
|
|
|
|
static cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b) |
|
|
|
|
{ |
|
|
|
|
USERATTS *ua, *ub; |
|
|
|
|
DATA_USERATTS(ua, a); |
|
|
|
|
DATA_USERATTS(ub, b); |
|
|
|
|
cmp_t c = CMP_BIGINT(ua->userid, ub->userid); |
|
|
|
|
if (c == 0) { |
|
|
|
|
c = CMP_STR(ua->attname, ub->attname); |
|
|
|
|
if (c == 0) |
|
|
|
|
c = CMP_TV(ub->expirydate, ua->expirydate); |
|
|
|
|
} |
|
|
|
|
return c; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Must be R or W locked before call
|
|
|
|
|
static K_ITEM *find_useratts(int64_t userid, char *attname) |
|
|
|
|
{ |
|
|
|
|
USERATTS useratts; |
|
|
|
|
K_TREE_CTX ctx[1]; |
|
|
|
|
K_ITEM look; |
|
|
|
|
|
|
|
|
|
useratts.userid = userid; |
|
|
|
|
STRNCPY(useratts.attname, attname); |
|
|
|
|
useratts.expirydate.tv_sec = default_expiry.tv_sec; |
|
|
|
|
useratts.expirydate.tv_usec = default_expiry.tv_usec; |
|
|
|
|
|
|
|
|
|
INIT_USERATTS(&look); |
|
|
|
|
look.data = (void *)(&useratts); |
|
|
|
|
return find_in_ktree(useratts_root, &look, cmp_useratts, ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begun) |
|
|
|
|
{ |
|
|
|
|
ExecStatusType rescode; |
|
|
|
|
bool conned = false; |
|
|
|
|
K_TREE_CTX ctx[1]; |
|
|
|
|
PGresult *res; |
|
|
|
|
K_ITEM *old_item; |
|
|
|
|
USERATTS *old_useratts, *useratts; |
|
|
|
|
char *upd, *ins; |
|
|
|
|
bool ok = false; |
|
|
|
|
char *params[9 + HISTORYDATECOUNT]; |
|
|
|
|
int n, par; |
|
|
|
|
|
|
|
|
|
LOGDEBUG("%s(): add", __func__); |
|
|
|
|
|
|
|
|
|
DATA_USERATTS(useratts, ua_item); |
|
|
|
|
|
|
|
|
|
K_RLOCK(useratts_free); |
|
|
|
|
old_item = find_useratts(useratts->userid, useratts->attname); |
|
|
|
|
K_RUNLOCK(useratts_free); |
|
|
|
|
DATA_USERATTS_NULL(old_useratts, old_item); |
|
|
|
|
|
|
|
|
|
/* N.B. the values of the old ua_item record, if it exists,
|
|
|
|
|
* are completely ignored i.e. you must provide all values required */ |
|
|
|
|
|
|
|
|
|
if (!conn) { |
|
|
|
|
conn = dbconnect(); |
|
|
|
|
conned = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!begun) { |
|
|
|
|
// Beginning of a write txn
|
|
|
|
|
res = PQexec(conn, "Begin", CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Begin", rescode, conn); |
|
|
|
|
goto unparam; |
|
|
|
|
} |
|
|
|
|
PQclear(res); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (old_item) { |
|
|
|
|
upd = "update useratts set expirydate=$1 where userid=$2 and " |
|
|
|
|
"attname=$3 and expirydate=$4"; |
|
|
|
|
par = 0; |
|
|
|
|
params[par++] = tv_to_buf(cd, NULL, 0); |
|
|
|
|
params[par++] = bigint_to_buf(old_useratts->userid, NULL, 0); |
|
|
|
|
params[par++] = str_to_buf(old_useratts->attname, NULL, 0); |
|
|
|
|
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0); |
|
|
|
|
PARCHKVAL(par, 4, params); |
|
|
|
|
|
|
|
|
|
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Update", rescode, conn); |
|
|
|
|
goto unparam; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (n = 0; n < par; n++) |
|
|
|
|
free(params[n]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
par = 0; |
|
|
|
|
params[par++] = bigint_to_buf(useratts->userid, NULL, 0); |
|
|
|
|
params[par++] = str_to_buf(useratts->attname, NULL, 0); |
|
|
|
|
params[par++] = str_to_buf(useratts->status, NULL, 0); |
|
|
|
|
params[par++] = str_to_buf(useratts->attstr, NULL, 0); |
|
|
|
|
params[par++] = str_to_buf(useratts->attstr2, NULL, 0); |
|
|
|
|
params[par++] = bigint_to_buf(useratts->attnum, NULL, 0); |
|
|
|
|
params[par++] = bigint_to_buf(useratts->attnum2, NULL, 0); |
|
|
|
|
params[par++] = tv_to_buf(&(useratts->attdate), NULL, 0); |
|
|
|
|
params[par++] = tv_to_buf(&(useratts->attdate2), NULL, 0); |
|
|
|
|
HISTORYDATEPARAMS(params, par, useratts); |
|
|
|
|
PARCHK(par, params); |
|
|
|
|
|
|
|
|
|
ins = "insert into useratts " |
|
|
|
|
"(userid,attname,status,attstr,attstr2,attnum,attnum2," |
|
|
|
|
"attdate,attdate2" |
|
|
|
|
HISTORYDATECONTROL ") values (" PQPARAM14 ")"; |
|
|
|
|
|
|
|
|
|
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
if (!begun) { |
|
|
|
|
PQclear(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Insert", rescode, conn); |
|
|
|
|
res = PQexec(conn, "Rollback", CKPQ_WRITE); |
|
|
|
|
goto unparam; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res = PQexec(conn, "Commit", CKPQ_WRITE); |
|
|
|
|
ok = true; |
|
|
|
|
} else { |
|
|
|
|
if (PGOK(rescode)) |
|
|
|
|
ok = true; |
|
|
|
|
} |
|
|
|
|
unparam: |
|
|
|
|
PQclear(res); |
|
|
|
|
if (conned) |
|
|
|
|
PQfinish(conn); |
|
|
|
|
for (n = 0; n < par; n++) |
|
|
|
|
free(params[n]); |
|
|
|
|
|
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
if (ok) { |
|
|
|
|
if (old_item) { |
|
|
|
|
useratts_root = remove_from_ktree(useratts_root, old_item, cmp_useratts, ctx); |
|
|
|
|
copy_tv(&(old_useratts->expirydate), cd); |
|
|
|
|
useratts_root = add_to_ktree(useratts_root, old_item, cmp_useratts); |
|
|
|
|
} |
|
|
|
|
useratts_root = add_to_ktree(useratts_root, ua_item, cmp_useratts); |
|
|
|
|
k_add_head(useratts_store, ua_item); |
|
|
|
|
} |
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
|
|
|
|
|
return ok; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static __maybe_unused K_ITEM *useratts_add(PGconn *conn, char *username, char *attname, |
|
|
|
|
char *status, char *attstr, char *attstr2, |
|
|
|
|
char *attnum, char *attnum2, char *attdate, |
|
|
|
|
char *attdate2, char *by, char *code, |
|
|
|
|
char *inet, tv_t *cd, K_TREE *trf_root, |
|
|
|
|
bool begun) |
|
|
|
|
{ |
|
|
|
|
K_ITEM *item, *u_item; |
|
|
|
|
USERATTS *row; |
|
|
|
|
USERS *users; |
|
|
|
|
bool ok = false; |
|
|
|
|
|
|
|
|
|
LOGDEBUG("%s(): add", __func__); |
|
|
|
|
|
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
item = k_unlink_head(useratts_free); |
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
DATA_USERATTS(row, item); |
|
|
|
|
|
|
|
|
|
K_RLOCK(users_free); |
|
|
|
|
u_item = find_users(username); |
|
|
|
|
K_RUNLOCK(users_free); |
|
|
|
|
if (!u_item) |
|
|
|
|
goto unitem; |
|
|
|
|
DATA_USERS(users, u_item); |
|
|
|
|
|
|
|
|
|
row->userid = users->userid; |
|
|
|
|
STRNCPY(row->attname, attname); |
|
|
|
|
if (status == NULL) |
|
|
|
|
row->status[0] = '\0'; |
|
|
|
|
else |
|
|
|
|
STRNCPY(row->status, status); |
|
|
|
|
if (attstr == NULL) |
|
|
|
|
row->attstr[0] = '\0'; |
|
|
|
|
else |
|
|
|
|
STRNCPY(row->attstr, attstr); |
|
|
|
|
if (attstr2 == NULL) |
|
|
|
|
row->attstr2[0] = '\0'; |
|
|
|
|
else |
|
|
|
|
STRNCPY(row->attstr2, attstr2); |
|
|
|
|
if (attnum == NULL || attnum[0] == '\0') |
|
|
|
|
row->attnum = 0; |
|
|
|
|
else |
|
|
|
|
TXT_TO_BIGINT("attnum", attnum, row->attnum); |
|
|
|
|
if (attnum2 == NULL || attnum2[0] == '\0') |
|
|
|
|
row->attnum2 = 0; |
|
|
|
|
else |
|
|
|
|
TXT_TO_BIGINT("attnum2", attnum2, row->attnum2); |
|
|
|
|
if (attdate == NULL || attdate[0] == '\0') |
|
|
|
|
row->attdate.tv_sec = row->attdate.tv_usec = 0L; |
|
|
|
|
else |
|
|
|
|
TXT_TO_TV("attdate", attdate, row->attdate); |
|
|
|
|
if (attdate2 == NULL || attdate2[0] == '\0') |
|
|
|
|
row->attdate2.tv_sec = row->attdate2.tv_usec = 0L; |
|
|
|
|
else |
|
|
|
|
TXT_TO_TV("attdate2", attdate2, row->attdate2); |
|
|
|
|
|
|
|
|
|
HISTORYDATEINIT(row, cd, by, code, inet); |
|
|
|
|
HISTORYDATETRANSFER(trf_root, row); |
|
|
|
|
|
|
|
|
|
ok = useratts_item_add(conn, item, cd, begun); |
|
|
|
|
unitem: |
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(useratts_free, item); |
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
|
|
|
|
|
if (ok) |
|
|
|
|
return item; |
|
|
|
|
else |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool useratts_fill(PGconn *conn) |
|
|
|
|
{ |
|
|
|
|
ExecStatusType rescode; |
|
|
|
|
PGresult *res; |
|
|
|
|
K_ITEM *item; |
|
|
|
|
int n, i; |
|
|
|
|
USERATTS *row; |
|
|
|
|
char *field; |
|
|
|
|
char *sel; |
|
|
|
|
int fields = 9; |
|
|
|
|
bool ok; |
|
|
|
|
|
|
|
|
|
LOGDEBUG("%s(): select", __func__); |
|
|
|
|
|
|
|
|
|
sel = "select " |
|
|
|
|
"userid,attname,status,attstr,attstr2,attnum,attnum2" |
|
|
|
|
",attdate,attdate2" |
|
|
|
|
HISTORYDATECONTROL |
|
|
|
|
" from useratts"; |
|
|
|
|
res = PQexec(conn, sel, CKPQ_READ); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Select", rescode, conn); |
|
|
|
|
PQclear(res); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
n = PQnfields(res); |
|
|
|
|
if (n != (fields + HISTORYDATECOUNT)) { |
|
|
|
|
LOGERR("%s(): Invalid field count - should be %d, but is %d", |
|
|
|
|
__func__, fields + HISTORYDATECOUNT, n); |
|
|
|
|
PQclear(res); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
n = PQntuples(res); |
|
|
|
|
LOGDEBUG("%s(): tree build count %d", __func__, n); |
|
|
|
|
ok = true; |
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
for (i = 0; i < n; i++) { |
|
|
|
|
item = k_unlink_head(useratts_free); |
|
|
|
|
DATA_USERATTS(row, item); |
|
|
|
|
|
|
|
|
|
if (everyone_die) { |
|
|
|
|
ok = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "userid", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_BIGINT("userid", field, row->userid); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attname", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_STR("attname", field, row->attname); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "status", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_STR("status", field, row->status); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attstr", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_STR("attstr", field, row->attstr); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attstr2", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_STR("attstr2", field, row->attstr2); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attnum", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_BIGINT("attnum", field, row->attnum); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attnum2", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_BIGINT("attnum2", field, row->attnum2); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attdate", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_TV("attdate", field, row->attdate); |
|
|
|
|
|
|
|
|
|
PQ_GET_FLD(res, i, "attdate2", field, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
TXT_TO_TV("attdate2", field, row->attdate2); |
|
|
|
|
|
|
|
|
|
HISTORYDATEFLDS(res, i, row, ok); |
|
|
|
|
if (!ok) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
useratts_root = add_to_ktree(useratts_root, item, cmp_useratts); |
|
|
|
|
k_add_head(useratts_store, item); |
|
|
|
|
} |
|
|
|
|
if (!ok) |
|
|
|
|
k_add_head(useratts_free, item); |
|
|
|
|
|
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
PQclear(res); |
|
|
|
|
|
|
|
|
|
if (ok) { |
|
|
|
|
LOGDEBUG("%s(): built", __func__); |
|
|
|
|
LOGWARNING("%s(): loaded %d useratts records", __func__, n); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ok; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void useratts_reload() |
|
|
|
|
{ |
|
|
|
|
PGconn *conn = dbconnect(); |
|
|
|
|
|
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
useratts_root = free_ktree(useratts_root, NULL); |
|
|
|
|
k_list_transfer_to_head(useratts_store, useratts_free); |
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
|
|
|
|
|
useratts_fill(conn); |
|
|
|
|
|
|
|
|
|
PQfinish(conn); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// order by userid asc,workername asc,expirydate desc
|
|
|
|
|
static cmp_t cmp_workers(K_ITEM *a, K_ITEM *b) |
|
|
|
|
{ |
|
|
|
@ -7674,6 +8026,8 @@ static bool getdata2()
|
|
|
|
|
if (!(ok = sharesummary_fill(conn)) || everyone_die) |
|
|
|
|
goto sukamudai; |
|
|
|
|
if (!confirm_sharesummary) { |
|
|
|
|
if (!(ok = useratts_fill(conn)) || everyone_die) |
|
|
|
|
goto sukamudai; |
|
|
|
|
if (!(ok = poolstats_fill(conn)) || everyone_die) |
|
|
|
|
goto sukamudai; |
|
|
|
|
ok = userstats_fill(conn); |
|
|
|
@ -7865,6 +8219,11 @@ static void alloc_storage()
|
|
|
|
|
users_root = new_ktree(); |
|
|
|
|
userid_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
useratts_free = k_new_list("Useratts", sizeof(USERATTS), |
|
|
|
|
ALLOC_USERATTS, LIMIT_USERATTS, true); |
|
|
|
|
useratts_store = k_new_store(useratts_free); |
|
|
|
|
useratts_root = new_ktree(); |
|
|
|
|
|
|
|
|
|
workers_free = k_new_list("Workers", sizeof(WORKERS), |
|
|
|
|
ALLOC_WORKERS, LIMIT_WORKERS, true); |
|
|
|
|
workers_store = k_new_store(workers_free); |
|
|
|
@ -8234,7 +8593,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
|
|
|
|
|
struckout: |
|
|
|
|
if (reason) { |
|
|
|
|
snprintf(reply, siz, "ERR.%s", reason); |
|
|
|
|
LOGERR("%s.%s", id, reply); |
|
|
|
|
LOGERR("%s.%s.%s", cmd, id, reply); |
|
|
|
|
return strdup(reply); |
|
|
|
|
} |
|
|
|
|
snprintf(reply, siz, "ok.%s", answer); |
|
|
|
@ -9902,6 +10261,304 @@ static char *cmd_homepage(__maybe_unused PGconn *conn, char *cmd, char *id,
|
|
|
|
|
return buf; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char *cmd_getatts(__maybe_unused PGconn *conn, char *cmd, char *id, |
|
|
|
|
tv_t *now, __maybe_unused char *by, |
|
|
|
|
__maybe_unused char *code, __maybe_unused char *inet, |
|
|
|
|
__maybe_unused tv_t *notcd, K_TREE *trf_root) |
|
|
|
|
{ |
|
|
|
|
K_ITEM *i_username, *i_attlist, *u_item, *ua_item; |
|
|
|
|
char reply[1024] = ""; |
|
|
|
|
size_t siz = sizeof(reply); |
|
|
|
|
char tmp[1024]; |
|
|
|
|
USERATTS *useratts; |
|
|
|
|
USERS *users; |
|
|
|
|
char *reason = NULL; |
|
|
|
|
char *answer = NULL; |
|
|
|
|
char *ptr, *comma, *dot; |
|
|
|
|
size_t len, off; |
|
|
|
|
bool first; |
|
|
|
|
|
|
|
|
|
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); |
|
|
|
|
|
|
|
|
|
i_username = require_name(trf_root, "username", 3, (char *)userpatt, reply, siz); |
|
|
|
|
if (!i_username) { |
|
|
|
|
reason = "Missing username"; |
|
|
|
|
goto nuts; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
K_RLOCK(users_free); |
|
|
|
|
u_item = find_users(transfer_data(i_username)); |
|
|
|
|
K_RUNLOCK(users_free); |
|
|
|
|
|
|
|
|
|
if (!u_item) { |
|
|
|
|
reason = "Unknown user"; |
|
|
|
|
goto nuts; |
|
|
|
|
} else { |
|
|
|
|
DATA_USERS(users, u_item); |
|
|
|
|
i_attlist = require_name(trf_root, "attlist", 1, NULL, reply, siz); |
|
|
|
|
if (!i_attlist) { |
|
|
|
|
reason = "Missing attlist"; |
|
|
|
|
goto nuts; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
APPEND_REALLOC_INIT(answer, off, len); |
|
|
|
|
ptr = strdup(transfer_data(i_attlist)); |
|
|
|
|
first = true; |
|
|
|
|
while (ptr && *ptr) { |
|
|
|
|
comma = strchr(ptr, ','); |
|
|
|
|
if (comma) |
|
|
|
|
*(comma++) = '\0'; |
|
|
|
|
dot = strchr(ptr, '.'); |
|
|
|
|
if (!dot) { |
|
|
|
|
free(answer); |
|
|
|
|
reason = "Missing element"; |
|
|
|
|
goto nuts; |
|
|
|
|
} |
|
|
|
|
*(dot++) = '\0'; |
|
|
|
|
K_RLOCK(useratts_free); |
|
|
|
|
ua_item = find_useratts(users->userid, ptr); |
|
|
|
|
K_RUNLOCK(useratts_free); |
|
|
|
|
/* web code must check the existance of the attname
|
|
|
|
|
* in the reply since it will be missing if it doesn't |
|
|
|
|
* exist in the DB */ |
|
|
|
|
if (ua_item) { |
|
|
|
|
char num_buf[BIGINT_BUFSIZ]; |
|
|
|
|
char ctv_buf[CDATE_BUFSIZ]; |
|
|
|
|
char *ans; |
|
|
|
|
DATA_USERATTS(useratts, ua_item); |
|
|
|
|
if (strcmp(dot, "str") == 0) { |
|
|
|
|
ans = useratts->attstr; |
|
|
|
|
} else if (strcmp(dot, "str2") == 0) { |
|
|
|
|
ans = useratts->attstr2; |
|
|
|
|
} else if (strcmp(dot, "num") == 0) { |
|
|
|
|
bigint_to_buf(useratts->attnum, |
|
|
|
|
num_buf, |
|
|
|
|
sizeof(num_buf)); |
|
|
|
|
ans = num_buf; |
|
|
|
|
} else if (strcmp(dot, "num2") == 0) { |
|
|
|
|
bigint_to_buf(useratts->attnum2, |
|
|
|
|
num_buf, |
|
|
|
|
sizeof(num_buf)); |
|
|
|
|
ans = num_buf; |
|
|
|
|
} else if (strcmp(dot, "date") == 0) { |
|
|
|
|
ctv_to_buf(&(useratts->attdate), |
|
|
|
|
ctv_buf, |
|
|
|
|
sizeof(num_buf)); |
|
|
|
|
ans = ctv_buf; |
|
|
|
|
} else if (strcmp(dot, "dateexp") == 0) { |
|
|
|
|
// Y/N if date is before now (not expired)
|
|
|
|
|
if (tv_newer(&(useratts->attdate), now)) |
|
|
|
|
ans = TRUE_STR; |
|
|
|
|
else |
|
|
|
|
ans = FALSE_STR; |
|
|
|
|
} else if (strcmp(dot, "date2") == 0) { |
|
|
|
|
ctv_to_buf(&(useratts->attdate2), |
|
|
|
|
ctv_buf, |
|
|
|
|
sizeof(num_buf)); |
|
|
|
|
ans = ctv_buf; |
|
|
|
|
} else if (strcmp(dot, "date2exp") == 0) { |
|
|
|
|
// Y/N if date2 is before now (not expired)
|
|
|
|
|
if (tv_newer(&(useratts->attdate2), now)) |
|
|
|
|
ans = TRUE_STR; |
|
|
|
|
else |
|
|
|
|
ans = FALSE_STR; |
|
|
|
|
} else { |
|
|
|
|
free(answer); |
|
|
|
|
reason = "Unknown element"; |
|
|
|
|
goto nuts; |
|
|
|
|
} |
|
|
|
|
snprintf(tmp, sizeof(tmp), "%s%s.%s=%s", |
|
|
|
|
first ? EMPTY : FLDSEPSTR, |
|
|
|
|
ptr, dot, ans); |
|
|
|
|
APPEND_REALLOC(answer, off, len, tmp); |
|
|
|
|
first = false; |
|
|
|
|
} |
|
|
|
|
ptr = comma; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
nuts: |
|
|
|
|
if (reason) { |
|
|
|
|
snprintf(reply, siz, "ERR.%s", reason); |
|
|
|
|
LOGERR("%s.%s.%s", cmd, id, reply); |
|
|
|
|
return strdup(reply); |
|
|
|
|
} |
|
|
|
|
snprintf(reply, siz, "ok.%s", answer); |
|
|
|
|
LOGDEBUG("%s.%s", id, answer); |
|
|
|
|
free(answer); |
|
|
|
|
return strdup(reply); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void att_to_date(tv_t *date, char *data, tv_t *now) |
|
|
|
|
{ |
|
|
|
|
int add; |
|
|
|
|
|
|
|
|
|
if (strncasecmp(data, "now+", 4) == 0) { |
|
|
|
|
add = atoi(data+4); |
|
|
|
|
copy_tv(date, now); |
|
|
|
|
date->tv_sec += add; |
|
|
|
|
} else if (strcasecmp(data, "now") == 0) { |
|
|
|
|
copy_tv(date, now); |
|
|
|
|
} else { |
|
|
|
|
txt_to_ctv("date", data, date, sizeof(*date)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static char *cmd_setatts(PGconn *conn, char *cmd, char *id, |
|
|
|
|
tv_t *now, char *by, char *code, char *inet, |
|
|
|
|
__maybe_unused tv_t *notcd, K_TREE *trf_root) |
|
|
|
|
{ |
|
|
|
|
ExecStatusType rescode; |
|
|
|
|
PGresult *res; |
|
|
|
|
bool conned = false; |
|
|
|
|
K_ITEM *i_username, *t_item, *u_item, *ua_item = NULL; |
|
|
|
|
K_TREE_CTX ctx[1]; |
|
|
|
|
char reply[1024] = ""; |
|
|
|
|
size_t siz = sizeof(reply); |
|
|
|
|
TRANSFER *transfer; |
|
|
|
|
USERATTS *useratts; |
|
|
|
|
USERS *users; |
|
|
|
|
char attname[sizeof(useratts->attname)*2]; |
|
|
|
|
char *reason = NULL; |
|
|
|
|
char *dot, *data; |
|
|
|
|
bool begun = false; |
|
|
|
|
|
|
|
|
|
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); |
|
|
|
|
|
|
|
|
|
i_username = require_name(trf_root, "username", 3, (char *)userpatt, reply, siz); |
|
|
|
|
if (!i_username) { |
|
|
|
|
reason = "Missing user"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
K_RLOCK(users_free); |
|
|
|
|
u_item = find_users(transfer_data(i_username)); |
|
|
|
|
K_RUNLOCK(users_free); |
|
|
|
|
|
|
|
|
|
if (!u_item) { |
|
|
|
|
reason = "Unknown user"; |
|
|
|
|
goto bats; |
|
|
|
|
} else { |
|
|
|
|
DATA_USERS(users, u_item); |
|
|
|
|
/* format is: ua_attname.element=value
|
|
|
|
|
* i.e. eash starts with the constant "ua_" |
|
|
|
|
* transfer will sort them so that any of the same attname |
|
|
|
|
* will be next to each other |
|
|
|
|
* thus can combine multiple elements for the same attname |
|
|
|
|
* into one single useratts record (as is mandatory) */ |
|
|
|
|
t_item = first_in_ktree(trf_root, ctx); |
|
|
|
|
while (t_item) { |
|
|
|
|
DATA_TRANSFER(transfer, t_item); |
|
|
|
|
if (strncmp(transfer->name, "ua_", 3) == 0) { |
|
|
|
|
data = transfer_data(t_item); |
|
|
|
|
STRNCPY(attname, transfer->name + 3); |
|
|
|
|
dot = strchr(attname, '.'); |
|
|
|
|
if (!dot) { |
|
|
|
|
reason = "Missing element"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
*(dot++) = '\0'; |
|
|
|
|
// If we already had a different one, save it to the DB
|
|
|
|
|
if (ua_item && strcmp(useratts->attname, attname) != 0) { |
|
|
|
|
if (conn == NULL) { |
|
|
|
|
conn = dbconnect(); |
|
|
|
|
conned = true; |
|
|
|
|
} |
|
|
|
|
if (!begun) { |
|
|
|
|
// Beginning of a write txn
|
|
|
|
|
res = PQexec(conn, "Begin", CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
PQclear(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Begin", rescode, conn); |
|
|
|
|
reason = "DBERR"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
begun = true; |
|
|
|
|
} |
|
|
|
|
if (useratts_item_add(conn, ua_item, now, begun)) |
|
|
|
|
ua_item = NULL; |
|
|
|
|
else { |
|
|
|
|
res = PQexec(conn, "Rollback", CKPQ_WRITE); |
|
|
|
|
PQclear(res); |
|
|
|
|
reason = "DBERR"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (!ua_item) { |
|
|
|
|
K_RLOCK(useratts_free); |
|
|
|
|
ua_item = k_unlink_head(useratts_free); |
|
|
|
|
K_RUNLOCK(useratts_free); |
|
|
|
|
DATA_USERATTS(useratts, ua_item); |
|
|
|
|
bzero(useratts, sizeof(*useratts)); |
|
|
|
|
useratts->userid = users->userid; |
|
|
|
|
STRNCPY(useratts->attname, attname); |
|
|
|
|
HISTORYDATEINIT(useratts, now, by, code, inet); |
|
|
|
|
HISTORYDATETRANSFER(trf_root, useratts); |
|
|
|
|
} |
|
|
|
|
if (strcmp(dot, "str") == 0) { |
|
|
|
|
STRNCPY(useratts->attstr, data); |
|
|
|
|
} else if (strcmp(dot, "str2") == 0) { |
|
|
|
|
STRNCPY(useratts->attstr2, data); |
|
|
|
|
} else if (strcmp(dot, "num") == 0) { |
|
|
|
|
TXT_TO_BIGINT("num", data, useratts->attnum); |
|
|
|
|
} else if (strcmp(dot, "num2") == 0) { |
|
|
|
|
TXT_TO_BIGINT("num2", data, useratts->attnum2); |
|
|
|
|
} else if (strcmp(dot, "date") == 0) { |
|
|
|
|
att_to_date(&(useratts->attdate), data, now); |
|
|
|
|
} else if (strcmp(dot, "date2") == 0) { |
|
|
|
|
att_to_date(&(useratts->attdate2), data, now); |
|
|
|
|
} else { |
|
|
|
|
reason = "Unknown element"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
t_item = next_in_ktree(ctx); |
|
|
|
|
} |
|
|
|
|
if (ua_item) { |
|
|
|
|
if (conn == NULL) { |
|
|
|
|
conn = dbconnect(); |
|
|
|
|
conned = true; |
|
|
|
|
} |
|
|
|
|
if (!begun) { |
|
|
|
|
// Beginning of a write txn
|
|
|
|
|
res = PQexec(conn, "Begin", CKPQ_WRITE); |
|
|
|
|
rescode = PQresultStatus(res); |
|
|
|
|
PQclear(res); |
|
|
|
|
if (!PGOK(rescode)) { |
|
|
|
|
PGLOGERR("Begin", rescode, conn); |
|
|
|
|
reason = "DBERR"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
begun = true; |
|
|
|
|
} |
|
|
|
|
if (!useratts_item_add(conn, ua_item, now, begun)) { |
|
|
|
|
res = PQexec(conn, "Rollback", CKPQ_WRITE); |
|
|
|
|
PQclear(res); |
|
|
|
|
reason = "DBERR"; |
|
|
|
|
goto bats; |
|
|
|
|
} |
|
|
|
|
res = PQexec(conn, "Commit", CKPQ_WRITE); |
|
|
|
|
PQclear(res); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
bats: |
|
|
|
|
if (conned) |
|
|
|
|
PQfinish(conn); |
|
|
|
|
if (reason) { |
|
|
|
|
if (ua_item) { |
|
|
|
|
K_WLOCK(useratts_free); |
|
|
|
|
k_add_head(useratts_free, ua_item); |
|
|
|
|
K_WUNLOCK(useratts_free); |
|
|
|
|
} |
|
|
|
|
snprintf(reply, siz, "ERR.%s", reason); |
|
|
|
|
LOGERR("%s.%s.%s", cmd, id, reply); |
|
|
|
|
return strdup(reply); |
|
|
|
|
} |
|
|
|
|
snprintf(reply, siz, "ok.set"); |
|
|
|
|
LOGDEBUG("%s.%s", id, reply); |
|
|
|
|
return strdup(reply); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// order by userid asc
|
|
|
|
|
static cmp_t cmp_mu(K_ITEM *a, K_ITEM *b) |
|
|
|
|
{ |
|
|
|
@ -10394,7 +11051,7 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id,
|
|
|
|
|
return buf; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: limit access
|
|
|
|
|
// TODO: limit access by having seperate sockets for each
|
|
|
|
|
#define ACCESS_POOL "p" |
|
|
|
|
#define ACCESS_SYSTEM "s" |
|
|
|
|
#define ACCESS_WEB "w" |
|
|
|
@ -10484,6 +11141,8 @@ static struct CMDS {
|
|
|
|
|
{ CMD_WORKERS, "workers", false, false, cmd_workers, ACCESS_WEB }, |
|
|
|
|
{ CMD_ALLUSERS, "allusers", false, false, cmd_allusers, ACCESS_WEB }, |
|
|
|
|
{ CMD_HOMEPAGE, "homepage", false, false, cmd_homepage, ACCESS_WEB }, |
|
|
|
|
{ CMD_GETATTS, "getatts", false, false, cmd_getatts, ACCESS_WEB }, |
|
|
|
|
{ CMD_SETATTS, "setatts", false, false, cmd_setatts, 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 }, |
|
|
|
@ -11152,6 +11811,7 @@ static void *socketer(__maybe_unused void *arg)
|
|
|
|
|
char *last_newpass = NULL, *reply_newpass = NULL; |
|
|
|
|
char *last_userset = NULL, *reply_userset = NULL; |
|
|
|
|
char *last_newid = NULL, *reply_newid = NULL; |
|
|
|
|
char *last_setatts = NULL, *reply_setatts = NULL; |
|
|
|
|
char *last_web = NULL, *reply_web = NULL; |
|
|
|
|
char *reply_last, duptype[CMD_SIZ+1]; |
|
|
|
|
enum cmd_values cmdnum; |
|
|
|
@ -11213,10 +11873,12 @@ static void *socketer(__maybe_unused void *arg)
|
|
|
|
|
* message is sent within the same second and thus |
|
|
|
|
* will effectively reduce the processing load for |
|
|
|
|
* sequential duplicates |
|
|
|
|
* adduser duplicates are handled by the DB code |
|
|
|
|
* auth, chkpass, adduser, newpass, newid - |
|
|
|
|
* remember individual last message and reply and repeat |
|
|
|
|
* the reply without reprocessing the message |
|
|
|
|
* As per the 'if' list below, |
|
|
|
|
* remember individual last messages and replies and |
|
|
|
|
* repeat the reply without reprocessing the message |
|
|
|
|
* The rest are remembered in the same buffer 'web' |
|
|
|
|
* so a duplicate will not be seen if another 'web' |
|
|
|
|
* command arrived between two duplicate commands |
|
|
|
|
*/ |
|
|
|
|
dup = false; |
|
|
|
|
// These are ordered approximately most likely first
|
|
|
|
@ -11236,7 +11898,13 @@ static void *socketer(__maybe_unused void *arg)
|
|
|
|
|
reply_last = reply_newid; |
|
|
|
|
dup = true; |
|
|
|
|
} else if (last_addrauth && strcmp(last_addrauth, buf) == 0) { |
|
|
|
|
reply_last = reply_auth; |
|
|
|
|
reply_last = reply_addrauth; |
|
|
|
|
dup = true; |
|
|
|
|
} else if (last_userset && strcmp(last_userset, buf) == 0) { |
|
|
|
|
reply_last = reply_userset; |
|
|
|
|
dup = true; |
|
|
|
|
} else if (last_setatts && strcmp(last_setatts, buf) == 0) { |
|
|
|
|
reply_last = reply_setatts; |
|
|
|
|
dup = true; |
|
|
|
|
} else if (last_web && strcmp(last_web, buf) == 0) { |
|
|
|
|
reply_last = reply_web; |
|
|
|
@ -11324,6 +11992,8 @@ static void *socketer(__maybe_unused void *arg)
|
|
|
|
|
case CMD_ADDUSER: |
|
|
|
|
case CMD_NEWPASS: |
|
|
|
|
case CMD_USERSET: |
|
|
|
|
case CMD_GETATTS: |
|
|
|
|
case CMD_SETATTS: |
|
|
|
|
case CMD_BLOCKLIST: |
|
|
|
|
case CMD_NEWID: |
|
|
|
|
case CMD_STATS: |
|
|
|
@ -11360,6 +12030,10 @@ static void *socketer(__maybe_unused void *arg)
|
|
|
|
|
case CMD_NEWID: |
|
|
|
|
STORELASTREPLY(newid); |
|
|
|
|
break; |
|
|
|
|
case CMD_SETATTS: |
|
|
|
|
STORELASTREPLY(setatts); |
|
|
|
|
break; |
|
|
|
|
// The rest
|
|
|
|
|
default: |
|
|
|
|
free(rep); |
|
|
|
|
} |
|
|
|
@ -11554,6 +12228,8 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
|
|
|
|
|
case CMD_WORKERS: |
|
|
|
|
case CMD_ALLUSERS: |
|
|
|
|
case CMD_HOMEPAGE: |
|
|
|
|
case CMD_GETATTS: |
|
|
|
|
case CMD_SETATTS: |
|
|
|
|
case CMD_DSP: |
|
|
|
|
case CMD_STATS: |
|
|
|
|
case CMD_PPLNS: |
|
|
|
|