Merge pull request #489 from ennorehling/hotfix/fix-faction-references

fix writing faction references
This commit is contained in:
Enno Rehling 2016-02-23 07:30:26 +01:00
commit d6a8d12dbb
36 changed files with 574 additions and 200 deletions

2
crypto

@ -1 +1 @@
Subproject commit e0f9891a91d69c042f82c1e13e48ab4c7160381d
Subproject commit 93dc9200fa4cb6bfa3883b19f6d33fd416ca43da

View File

@ -1,3 +1,13 @@
{
strcpy.S:197
Memcheck:Cond
obj:/lib/x86_64-linux-gnu/libc-2.13.so
}
{
strcpy.S:1106
Memcheck:Value8
obj:/lib/x86_64-linux-gnu/libc-2.13.so
}
{
stpncpy sse3
Memcheck:Cond

View File

@ -1,3 +1,8 @@
{
stpncpy in strcpy-sse2-unaligned.S:1659
Memcheck:Value8
fun:__stpncpy_sse2_unaligned
}
# old zlib version
{
zlib1g-dev-1:1.2.3.4.dfsg

View File

@ -58,7 +58,8 @@ attrib_type at_otherfaction = {
struct faction *get_otherfaction(const struct attrib *a)
{
return (faction *)(a->data.v);
faction * f = (faction *)(a->data.v);
return (f && f->_alive) ? f : NULL;
}
struct attrib *make_otherfaction(struct faction *f)

View File

@ -228,21 +228,11 @@ static void message_faction(battle * b, faction * f, struct message *m)
void message_all(battle * b, message * m)
{
bfaction *bf;
plane *p = rplane(b->region);
watcher *w;
for (bf = b->factions; bf; bf = bf->next) {
assert(bf->faction);
message_faction(b, bf->faction, m);
}
if (p)
for (w = p->watchers; w; w = w->next) {
for (bf = b->factions; bf; bf = bf->next)
if (bf->faction == w->faction)
break;
if (bf == NULL)
message_faction(b, w->faction, m);
}
}
static void fbattlerecord(battle * b, faction * f, const char *s)

View File

@ -384,15 +384,16 @@ static int tolua_faction_create(lua_State * L)
static int tolua_faction_get_password(lua_State * L)
{
unused_arg(L);
return 0;
faction *self = (faction *)tolua_tousertype(L, 1, 0);
tolua_pushstring(L, self->_password);
return 1;
}
static int tolua_faction_set_password(lua_State * L)
{
faction *self = (faction *)tolua_tousertype(L, 1, 0);
const char * passw = tolua_tostring(L, 2, 0);
faction_setpassword(self, password_hash(passw, 0, PASSWORD_DEFAULT));
faction_setpassword(self, password_encode(passw, PASSWORD_DEFAULT));
return 0;
}

View File

@ -153,8 +153,8 @@ static int tolua_unit_get_group(lua_State * L)
static int tolua_unit_set_group(lua_State * L)
{
unit *self = (unit *)tolua_tousertype(L, 1, 0);
int result = join_group(self, tolua_tostring(L, 2, 0));
lua_pushinteger(L, result);
group *g = join_group(self, tolua_tostring(L, 2, 0));
lua_pushboolean(L, g!=NULL);
return 1;
}

View File

@ -1,3 +1,3 @@
#define VERSION_MAJOR 3
#define VERSION_MINOR 8
#define VERSION_BUILD 3
#define VERSION_BUILD 4

View File

@ -4,6 +4,7 @@
#include "alliance.h"
#include <CuTest.h>
#include <tests.h>
#include <quicklist.h>
#include <assert.h>
@ -61,9 +62,31 @@ static void test_alliance_join(CuTest *tc) {
test_cleanup();
}
static void test_alliance_dead_faction(CuTest *tc) {
faction *f, *f2;
alliance *al;
test_cleanup();
f = test_create_faction(0);
f2 = test_create_faction(0);
al = makealliance(42, "Hodor");
setalliance(f, al);
setalliance(f2, al);
CuAssertPtrEquals(tc, f, alliance_get_leader(al));
CuAssertIntEquals(tc, 2, ql_length(al->members));
CuAssertPtrEquals(tc, al, f->alliance);
destroyfaction(&factions);
CuAssertIntEquals(tc, 1, ql_length(al->members));
CuAssertPtrEquals(tc, f2, alliance_get_leader(al));
CuAssertPtrEquals(tc, NULL, f->alliance);
CuAssertTrue(tc, !f->_alive);
test_cleanup();
}
CuSuite *get_alliance_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_alliance_dead_faction);
SUITE_ADD_TEST(suite, test_alliance_make);
SUITE_ADD_TEST(suite, test_alliance_join);
return suite;

View File

@ -217,6 +217,7 @@ int resolve_faction(variant id, void *address)
result = -1;
}
}
assert(address);
*(faction **)address = f;
return result;
}
@ -252,7 +253,7 @@ faction *addfaction(const char *email, const char *password,
}
if (!password) password = itoa36(rng_int());
faction_setpassword(f, password_hash(password, 0, PASSWORD_DEFAULT));
faction_setpassword(f, password_encode(password, PASSWORD_DEFAULT));
ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
f->alliance_joindate = turn;
@ -333,7 +334,8 @@ variant read_faction_reference(struct storage * store)
void write_faction_reference(const faction * f, struct storage *store)
{
WRITE_INT(store, (f && f->_alive) ? f->no : 0);
assert(!f || f->_alive);
WRITE_INT(store, f ? f->no : 0);
}
static faction *dead_factions;
@ -566,7 +568,8 @@ void faction_setbanner(faction * self, const char *banner)
void faction_setpassword(faction * f, const char *pwhash)
{
assert(pwhash && pwhash[0] == '$');
assert(pwhash);
// && pwhash[0] == '$');
free(f->_password);
f->_password = _strdup(pwhash);
}

View File

@ -108,12 +108,12 @@ static void test_addfaction(CuTest *tc) {
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
CuAssertPtrEquals(tc, (void *)factions, (void *)f);
CuAssertStrEquals(tc, "test@eressea.de", f->email);
CuAssertIntEquals(tc, true, checkpasswd(f, "hurrdurr"));
CuAssertTrue(tc, checkpasswd(f, "hurrdurr"));
CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
CuAssertIntEquals(tc, 1234, f->subscription);
CuAssertIntEquals(tc, 0, f->flags);
CuAssertIntEquals(tc, 0, f->age);
CuAssertIntEquals(tc, true, faction_alive(f));
CuAssertTrue(tc, faction_alive(f));
CuAssertIntEquals(tc, M_GRAY, f->magiegebiet);
CuAssertIntEquals(tc, turn, f->lastorders);
CuAssertPtrEquals(tc, f, findfaction(f->no));
@ -124,10 +124,10 @@ static void test_check_passwd(CuTest *tc) {
faction *f;
f = test_create_faction(0);
faction_setpassword(f, password_hash("password", 0, PASSWORD_DEFAULT));
CuAssertIntEquals(tc, true, checkpasswd(f, "password"));
CuAssertIntEquals(tc, false, checkpasswd(f, "assword"));
CuAssertIntEquals(tc, false, checkpasswd(f, "PASSWORD"));
faction_setpassword(f, password_encode("password", PASSWORD_DEFAULT));
CuAssertTrue(tc, checkpasswd(f, "password"));
CuAssertTrue(tc, !checkpasswd(f, "assword"));
CuAssertTrue(tc, !checkpasswd(f, "PASSWORD"));
}
static void test_get_monsters(CuTest *tc) {

View File

@ -179,7 +179,7 @@ void set_group(struct unit *u, struct group *g)
}
}
bool join_group(unit * u, const char *name)
group *join_group(unit * u, const char *name)
{
group *g = NULL;
@ -192,7 +192,7 @@ bool join_group(unit * u, const char *name)
}
set_group(u, g);
return true;
return g;
}
void write_groups(struct storage *store, const faction * f)
@ -203,12 +203,12 @@ void write_groups(struct storage *store, const faction * f)
WRITE_INT(store, g->gid);
WRITE_STR(store, g->name);
for (a = g->allies; a; a = a->next) {
if (a->faction) {
if (a->faction && a->faction->_alive) {
write_faction_reference(a->faction, store);
WRITE_INT(store, a->status);
}
}
WRITE_INT(store, 0);
write_faction_reference(NULL, store);
a_write(store, g->attribs, g);
WRITE_SECTION(store);
}
@ -233,7 +233,7 @@ void read_groups(struct storage *store, faction * f)
ally *a;
variant fid;
READ_INT(store, &fid.i);
fid = read_faction_reference(store);
if (fid.i <= 0)
break;
a = ally_add(pa, findfaction(fid.i));

View File

@ -36,7 +36,7 @@ extern "C" {
} group;
extern struct attrib_type at_group; /* attribute for units assigned to a group */
extern bool join_group(struct unit *u, const char *name);
extern struct group *join_group(struct unit *u, const char *name);
extern void set_group(struct unit *u, struct group *g);
extern struct group * get_group(const struct unit *u);
extern void free_group(struct group *g);

View File

@ -5,6 +5,8 @@
#include "faction.h"
#include "unit.h"
#include "region.h"
#include <util/attrib.h>
#include <attributes/key.h>
#include <stream.h>
#include <filestream.h>
#include <storage.h>
@ -22,34 +24,47 @@ static void test_group_readwrite(CuTest * tc)
storage store;
FILE *F;
stream strm;
int i;
F = fopen("test.dat", "w");
F = fopen("test.dat", "wb");
fstream_init(&strm, F);
binstore_init(&store, &strm);
test_cleanup();
test_create_world();
f = test_create_faction(0);
g = new_group(f, "test", 42);
g = new_group(f, "NW", 42);
g = new_group(f, "Egoisten", 43);
a_add(&g->attribs, make_key(44));
al = ally_add(&g->allies, f);
al->status = HELP_GIVE;
write_groups(&store, f);
WRITE_INT(&store, 47);
binstore_done(&store);
fstream_done(&strm);
F = fopen("test.dat", "r");
F = fopen("test.dat", "rb");
fstream_init(&strm, F);
binstore_init(&store, &strm);
f->groups = 0;
free_group(g);
read_groups(&store, f);
READ_INT(&store, &i);
binstore_done(&store);
fstream_done(&strm);
CuAssertIntEquals(tc, 47, i);
CuAssertPtrNotNull(tc, f->groups);
CuAssertPtrNotNull(tc, f->groups->allies);
CuAssertPtrEquals(tc, 0, f->groups->allies->next);
CuAssertPtrEquals(tc, f, f->groups->allies->faction);
CuAssertIntEquals(tc, HELP_GIVE, f->groups->allies->status);
CuAssertIntEquals(tc, 42, f->groups->gid);
CuAssertStrEquals(tc, "NW", f->groups->name);
CuAssertPtrNotNull(tc, f->groups->next);
CuAssertIntEquals(tc, 43, f->groups->next->gid);
CuAssertStrEquals(tc, "Egoisten", f->groups->next->name);
CuAssertPtrEquals(tc, 0, f->groups->allies);
g = f->groups->next;
CuAssertPtrNotNull(tc, find_key(g->attribs, 44));
CuAssertPtrNotNull(tc, g->allies);
CuAssertPtrEquals(tc, 0, g->allies->next);
CuAssertPtrEquals(tc, f, g->allies->faction);
CuAssertIntEquals(tc, HELP_GIVE, g->allies->status);
remove("test.dat");
test_cleanup();
}
@ -68,8 +83,8 @@ static void test_group(CuTest * tc)
assert(r && f);
u = test_create_unit(f, r);
assert(u);
CuAssertTrue(tc, join_group(u, "hodor"));
CuAssertPtrNotNull(tc, (g = get_group(u)));
CuAssertPtrNotNull(tc, (g = join_group(u, "hodor")));
CuAssertPtrEquals(tc, g, get_group(u));
CuAssertStrEquals(tc, "hodor", g->name);
CuAssertIntEquals(tc, 1, g->members);
set_group(u, 0);

View File

@ -290,14 +290,3 @@ int read_plane_reference(plane ** pp, struct storage *store)
ur_add(id, pp, resolve_plane);
return AT_READ_OK;
}
bool is_watcher(const struct plane * p, const struct faction * f)
{
struct watcher *w;
if (!p)
return false;
w = p->watchers;
while (w && w->faction != f)
w = w->next;
return (w != NULL);
}

View File

@ -22,7 +22,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" {
#endif
struct faction;
struct region;
struct faction;
struct plane;
struct storage;
@ -43,15 +45,8 @@ extern "C" {
#define PFL_NOMONSTERS 16384 /* no monster randenc */
#define PFL_SEESPECIAL 32768 /* far seeing */
typedef struct watcher {
struct watcher *next;
struct faction *faction;
unsigned char mode;
} watcher;
typedef struct plane {
struct plane *next;
struct watcher *watchers;
int id;
char *name;
int minx, maxx, miny, maxy;
@ -76,7 +71,6 @@ extern "C" {
struct plane *get_homeplane(void);
extern int rel_to_abs(const struct plane *pl, const struct faction *f,
int rel, unsigned char index);
extern bool is_watcher(const struct plane *p, const struct faction *f);
extern void write_plane_reference(const plane * p, struct storage *store);
extern int read_plane_reference(plane ** pp, struct storage *store);
extern int plane_width(const plane * pl);

View File

@ -66,6 +66,7 @@ extern "C" {
struct message_list;
struct rawmaterial;
struct item;
struct faction;
#define MORALE_TAX_FACTOR 0.005 /* 0.5% tax per point of morale */
#define MORALE_MAX 10 /* Maximum morale allowed */

View File

@ -454,6 +454,7 @@ void write_alliances(struct gamedata *data)
alliance *al = alliances;
while (al) {
if (al->_leader) {
assert(al->_leader->_alive);
WRITE_INT(data->store, al->id);
WRITE_STR(data->store, al->name);
WRITE_INT(data->store, (int)al->flags);
@ -462,7 +463,7 @@ void write_alliances(struct gamedata *data)
}
al = al->next;
}
WRITE_INT(data->store, 0);
write_faction_reference(NULL, data->store);
WRITE_SECTION(data->store);
}
@ -535,11 +536,16 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
static void write_owner(struct gamedata *data, region_owner * owner)
{
if (owner) {
faction *f;
WRITE_INT(data->store, owner->since_turn);
WRITE_INT(data->store, owner->morale_turn);
WRITE_INT(data->store, owner->flags);
write_faction_reference(owner->last_owner, data->store);
write_faction_reference(owner->owner, data->store);
f = owner->last_owner;
write_faction_reference((f && f->_alive) ? f : NULL, data->store);
// TODO: check that destroyfaction does the right thing.
// TODO: What happens to morale when the owner dies?
f = owner->owner;
write_faction_reference((f && f->_alive) ? f : NULL, data->store);
}
else {
WRITE_INT(data->store, -1);
@ -612,8 +618,8 @@ unit *read_unit(struct gamedata *data)
uhash(u);
}
READ_INT(data->store, &n);
f = findfaction(n);
resolve_faction(read_faction_reference(data->store), &f);
assert(f);
if (f != u->faction) {
u_setfaction(u, f);
}
@ -761,6 +767,7 @@ void write_unit(struct gamedata *data, const unit * u)
const race *irace = u_irace(u);
write_unit_reference(u, data->store);
assert(u->faction->_alive);
write_faction_reference(u->faction, data->store);
WRITE_STR(data->store, u->_name);
WRITE_STR(data->store, u->display ? u->display : "");
@ -1154,6 +1161,58 @@ void write_spellbook(const struct spellbook *book, struct storage *store)
WRITE_TOK(store, "end");
}
static char * getpasswd(int fno) {
const char *prefix = itoa36(fno);
size_t len = strlen(prefix);
FILE * F = fopen("passwords.txt", "r");
char line[80];
if (F) {
while (!feof(F)) {
fgets(line, sizeof(line), F);
if (line[len]==':' && strncmp(prefix, line, len)==0) {
size_t slen = strlen(line)-1;
assert(line[slen]=='\n');
line[slen] = 0;
fclose(F);
return _strdup(line+len+1);
}
}
fclose(F);
}
return NULL;
}
static void read_password(gamedata *data, faction *f) {
char name[128];
READ_STR(data->store, name, sizeof(name));
if (data->version == BADCRYPT_VERSION) {
char * pass = getpasswd(f->no);
if (pass) {
faction_setpassword(f, password_encode(pass, PASSWORD_DEFAULT));
free(pass); // TODO: remove this allocation!
}
else {
free(f->_password);
f->_password = NULL;
}
}
else {
faction_setpassword(f, (data->version >= CRYPT_VERSION) ? name : password_encode(name, PASSWORD_DEFAULT));
}
}
void _test_read_password(gamedata *data, faction *f) {
read_password(data, f);
}
static void write_password(gamedata *data, const faction *f) {
WRITE_TOK(data->store, (const char *)f->_password);
}
void _test_write_password(gamedata *data, const faction *f) {
write_password(data, f);
}
/** Reads a faction from a file.
* This function requires no context, can be called in any state. The
* faction may not already exist, however.
@ -1162,14 +1221,14 @@ faction *readfaction(struct gamedata * data)
{
ally **sfp;
int planes, n;
faction *f;
faction *f = NULL;
char name[DISPLAYSIZE];
variant var;
READ_INT(data->store, &n);
f = findfaction(n);
resolve_faction(var = read_faction_reference(data->store), &f);
if (f == NULL) {
f = (faction *)calloc(1, sizeof(faction));
f->no = n;
f->no = var.i;
}
else {
f->allies = NULL; /* mem leak */
@ -1220,8 +1279,7 @@ faction *readfaction(struct gamedata * data)
set_email(&f->email, "");
}
READ_STR(data->store, name, sizeof(name));
faction_setpassword(f, (data->version > CRYPT_VERSION) ? name : password_hash(name, 0, PASSWORD_DEFAULT));
read_password(data, f);
if (data->version < NOOVERRIDE_VERSION) {
READ_STR(data->store, 0, 0);
}
@ -1308,6 +1366,7 @@ void writefaction(struct gamedata *data, const faction * f)
ally *sf;
ursprung *ur;
assert(f->_alive);
write_faction_reference(f, data->store);
WRITE_INT(data->store, f->subscription);
#if RELEASE_VERSION >= SPELL_LEVEL_VERSION
@ -1328,7 +1387,7 @@ void writefaction(struct gamedata *data, const faction * f)
WRITE_STR(data->store, f->name);
WRITE_STR(data->store, f->banner);
WRITE_STR(data->store, f->email);
WRITE_TOK(data->store, f->_password);
write_password(data, f);
WRITE_TOK(data->store, locale_name(f->locale));
WRITE_INT(data->store, f->lastorders);
WRITE_INT(data->store, f->age);
@ -1354,20 +1413,18 @@ void writefaction(struct gamedata *data, const faction * f)
WRITE_SECTION(data->store);
for (sf = f->allies; sf; sf = sf->next) {
int no;
int status;
faction *fa = sf->faction;
assert(sf->faction);
no = sf->faction->no;
status = alliedfaction(NULL, f, sf->faction, HELP_ALL);
if (status != 0) {
WRITE_INT(data->store, no);
WRITE_INT(data->store, sf->status);
assert(fa);
if (fa->_alive) {
int status = alliedfaction(NULL, f, fa, HELP_ALL);
if (status != 0) {
write_faction_reference(fa, data->store);
WRITE_INT(data->store, sf->status);
}
}
}
WRITE_INT(data->store, 0);
write_faction_reference(NULL, data->store);
WRITE_SECTION(data->store);
write_groups(data->store, f);
write_spellbook(f->spellbook, data->store);
@ -1463,7 +1520,6 @@ int readgame(const char *filename, bool backup)
READ_INT(&store, &nread);
while (--nread >= 0) {
int id;
variant fno;
plane *pl;
READ_INT(&store, &id);
@ -1500,15 +1556,12 @@ int readgame(const char *filename, bool backup)
}
}
else {
fno = read_faction_reference(&store);
while (fno.i) {
watcher *w = (watcher *)malloc(sizeof(watcher));
ur_add(fno, &w->faction, resolve_faction);
READ_INT(&store, &n);
w->mode = (unsigned char)n;
w->next = pl->watchers;
pl->watchers = w;
fno = read_faction_reference(&store);
/* WATCHERS - eliminated in February 2016, ca. turn 966 */
if (gdata.version < CRYPT_VERSION) {
variant fno;
do {
fno = read_faction_reference(&store);
} while (fno.i);
}
}
a_read(&store, &pl->attribs, pl);
@ -1789,7 +1842,6 @@ int writegame(const char *filename)
WRITE_SECTION(&store);
for (pl = planes; pl; pl = pl->next) {
watcher *w;
WRITE_INT(&store, pl->id);
WRITE_STR(&store, pl->name);
WRITE_INT(&store, pl->minx);
@ -1797,15 +1849,9 @@ int writegame(const char *filename)
WRITE_INT(&store, pl->miny);
WRITE_INT(&store, pl->maxy);
WRITE_INT(&store, pl->flags);
w = pl->watchers;
while (w) {
if (w->faction) {
write_faction_reference(w->faction, &store);
WRITE_INT(&store, w->mode);
}
w = w->next;
}
write_faction_reference(NULL, &store); /* mark the end of the list */
#if RELEASE_VERSION < CRYPT_VERSION
write_faction_reference(NULL, &store); /* mark the end of pl->watchers (gone since T966) */
#endif
a_write(&store, pl->attribs, pl);
WRITE_SECTION(&store);
}

View File

@ -83,6 +83,9 @@ extern "C" {
struct gamedata *gamedata_open(const char *filename, const char *mode);
void gamedata_close(struct gamedata *data);
/* test-only functions that give access to internal implementation details (BAD) */
void _test_write_password(struct gamedata *data, const struct faction *f);
void _test_read_password(struct gamedata *data, struct faction *f);
#ifdef __cplusplus
}
#endif

View File

@ -3,11 +3,25 @@
#include "save.h"
#include "unit.h"
#include "group.h"
#include "ally.h"
#include "faction.h"
#include "plane.h"
#include "region.h"
#include "version.h"
#include <triggers/changefaction.h>
#include <triggers/createunit.h>
#include <triggers/timeout.h>
#include <util/attrib.h>
#include <util/event.h>
#include <util/base36.h>
#include <util/password.h>
#include <CuTest.h>
#include <tests.h>
#include <storage.h>
#include <stdio.h>
static void test_readwrite_data(CuTest * tc)
@ -42,15 +56,17 @@ static void test_readwrite_unit(CuTest * tc)
u = test_create_unit(f, r);
join_path(datapath(), filename, path, sizeof(path));
data = gamedata_open(path, "w");
CuAssertPtrNotNull(tc, data); // TODO: intermittent test
data = gamedata_open(path, "wb");
CuAssertPtrNotNull(tc, data);
write_unit(data, u);
gamedata_close(data);
free_gamedata();
f = test_create_faction(0);
renumber_faction(f, fno);
data = gamedata_open(path, "r");
data = gamedata_open(path, "rb");
CuAssertPtrNotNull(tc, data);
u = read_unit(data);
gamedata_close(data);
@ -61,10 +77,189 @@ static void test_readwrite_unit(CuTest * tc)
test_cleanup();
}
static void test_readwrite_dead_faction_group(CuTest *tc) {
faction *f, *f2;
unit * u;
group *g;
ally *al;
int fno;
test_cleanup();
f = test_create_faction(0);
fno = f->no;
CuAssertPtrEquals(tc, f, factions);
CuAssertPtrEquals(tc, 0, f->next);
f2 = test_create_faction(0);
CuAssertPtrEquals(tc, f2, factions->next);
u = test_create_unit(f2, test_create_region(0, 0, 0));
CuAssertPtrNotNull(tc, u);
g = join_group(u, "group");
CuAssertPtrNotNull(tc, g);
al = ally_add(&g->allies, f);
CuAssertPtrNotNull(tc, al);
CuAssertPtrEquals(tc, f, factions);
destroyfaction(&factions);
CuAssertTrue(tc, !f->_alive);
CuAssertPtrEquals(tc, f2, factions);
writegame("test.dat");
free_gamedata();
f = f2 = NULL;
readgame("test.dat", false);
CuAssertPtrEquals(tc, 0, findfaction(fno));
f2 = factions;
CuAssertPtrNotNull(tc, f2);
u = f2->units;
CuAssertPtrNotNull(tc, u);
g = get_group(u);
CuAssertPtrNotNull(tc, g);
CuAssertPtrEquals(tc, 0, g->allies);
test_cleanup();
}
static void test_readwrite_dead_faction_regionowner(CuTest *tc) {
faction *f;
region *r;
test_cleanup();
config_set("rules.region_owners", "1");
f = test_create_faction(0);
test_create_unit(f, r = test_create_region(0, 0, 0));
region_set_owner(r, f, turn);
destroyfaction(&factions);
CuAssertTrue(tc, !f->_alive);
remove_empty_units();
writegame("test.dat");
free_gamedata();
f = NULL;
readgame("test.dat", false);
f = factions;
CuAssertPtrEquals(tc, 0, f);
r = regions;
CuAssertPtrNotNull(tc, r);
CuAssertPtrEquals(tc, 0, region_get_owner(r));
test_cleanup();
}
static void test_readwrite_dead_faction_changefaction(CuTest *tc) {
faction *f, *f2;
region *r;
trigger *tr;
unit * u;
test_cleanup();
f = test_create_faction(0);
f2 = test_create_faction(0);
u = test_create_unit(f2, r = test_create_region(0, 0, 0));
tr = trigger_changefaction(u, f);
add_trigger(&u->attribs, "timer", trigger_timeout(10, tr));
CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler));
destroyfaction(&factions);
CuAssertTrue(tc, !f->_alive);
remove_empty_units();
writegame("test.dat");
free_gamedata();
f = NULL;
readgame("test.dat", false);
f = factions;
CuAssertPtrNotNull(tc, f);
r = regions;
CuAssertPtrNotNull(tc, r);
u = r->units;
CuAssertPtrNotNull(tc, u);
CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler));
test_cleanup();
}
static void test_readwrite_dead_faction_createunit(CuTest *tc) {
faction *f, *f2;
region *r;
trigger *tr;
unit * u;
test_cleanup();
f = test_create_faction(0);
f2 = test_create_faction(0);
u = test_create_unit(f2, r = test_create_region(0, 0, 0));
tr = trigger_createunit(r, f, f->race, 1);
add_trigger(&u->attribs, "timer", trigger_timeout(10, tr));
CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler));
destroyfaction(&factions);
CuAssertTrue(tc, !f->_alive);
remove_empty_units();
writegame("test.dat");
free_gamedata();
f = NULL;
readgame("test.dat", false);
f = factions;
CuAssertPtrNotNull(tc, f);
r = regions;
CuAssertPtrNotNull(tc, r);
u = r->units;
CuAssertPtrNotNull(tc, u);
CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler));
test_cleanup();
}
static void test_read_password(CuTest *tc) {
const char *path = "test.dat";
gamedata *data;
faction *f;
f = test_create_faction(0);
faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT));
data = gamedata_open(path, "wb");
CuAssertPtrNotNull(tc, data);
_test_write_password(data, f);
gamedata_close(data);
data = gamedata_open(path, "rb");
CuAssertPtrNotNull(tc, data);
_test_read_password(data, f);
gamedata_close(data);
CuAssertTrue(tc, checkpasswd(f, "secret"));
CuAssertIntEquals(tc, 0, remove(path));
}
static void test_read_password_external(CuTest *tc) {
const char *path = "test.dat", *pwfile = "passwords.txt";
gamedata *data;
faction *f;
FILE * F;
remove(pwfile);
f = test_create_faction(0);
faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT));
CuAssertPtrNotNull(tc, f->_password);
data = gamedata_open(path, "wb");
CuAssertPtrNotNull(tc, data);
WRITE_TOK(data->store, (const char *)f->_password);
WRITE_TOK(data->store, (const char *)f->_password);
gamedata_close(data);
data = gamedata_open(path, "rb");
CuAssertPtrNotNull(tc, data);
data->version = BADCRYPT_VERSION;
_test_read_password(data, f);
CuAssertPtrEquals(tc, 0, f->_password);
F = fopen(pwfile, "wt");
fprintf(F, "%s:secret\n", itoa36(f->no));
fclose(F);
_test_read_password(data, f);
CuAssertPtrNotNull(tc, f->_password);
gamedata_close(data);
CuAssertTrue(tc, checkpasswd(f, "secret"));
CuAssertIntEquals(tc, 0, remove(path));
CuAssertIntEquals(tc, 0, remove(pwfile));
}
CuSuite *get_save_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_readwrite_data);
SUITE_ADD_TEST(suite, test_readwrite_unit);
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_createunit);
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction);
SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner);
DISABLE_TEST(suite, test_readwrite_dead_faction_group);
SUITE_ADD_TEST(suite, test_read_password);
SUITE_ADD_TEST(suite, test_read_password_external);
return suite;
}

View File

@ -33,9 +33,10 @@
#define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */
#define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */
#define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */
#define CRYPT_VERSION 351 /* passwords are encrypted */
#define RELEASE_VERSION CRYPT_VERSION /* current datafile */
#define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */
#define CRYPT_VERSION 352 /* passwords are encrypted */
#define RELEASE_VERSION ATTRIBOWNER_VERSION /* current datafile */
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */
#define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */
#define MAX_VERSION BADCRYPT_VERSION /* change this if we can need to read the future datafile, and we can do so */
#define STREAM_VERSION 2 /* internal encoding of binary files */

View File

@ -2172,7 +2172,7 @@ int password_cmd(unit * u, struct order *ord)
cmistake(u, ord, 283, MSG_EVENT);
strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf));
}
faction_setpassword(u->faction, password_hash(pwbuf, 0, PASSWORD_DEFAULT));
faction_setpassword(u->faction, password_encode(pwbuf, PASSWORD_DEFAULT));
ADDMSG(&u->faction->msgs, msg_message("changepasswd",
"value", pwbuf));
return 0;

View File

@ -1385,17 +1385,9 @@ static void prepare_reports(void)
for (r = regions; r; r = r->next) {
unit *u;
plane *p = rplane(r);
reorder_units(r);
if (p) {
watcher *w = p->watchers;
for (; w; w = w->next) {
faction_add_seen(w->faction, r, w->mode);
}
}
/* Region owner get always the Lighthouse report */
if (bt_lighthouse && config_token("rules.region_owner_pay_building", bt_lighthouse->_name)) {
for (b = rbuildings(r); b; b = b->next) {
@ -1464,8 +1456,6 @@ static region *lastregion(faction * f)
/* we continue from the best region and look for travelthru etc. */
for (r = f->last->next; r; r = r->next) {
plane *p = rplane(r);
/* search the region for travelthru-attributes: */
if (fval(r, RF_TRAVELUNIT)) {
travelthru_map(r, cb_set_last, f);
@ -1474,9 +1464,6 @@ static region *lastregion(faction * f)
continue;
if (check_leuchtturm(r, f))
f->last = r;
if (p && is_watcher(p, f)) {
f->last = r;
}
}
return f->last->next;
#else

View File

@ -5,6 +5,7 @@
#include "prefix.h"
#include <kernel/config.h>
#include <kernel/plane.h>
#include <kernel/region.h>
#include <kernel/terrain.h>
#include <kernel/item.h>
@ -47,7 +48,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain)
{
region *r = findregion(x, y);
if (!r) {
r = new_region(x, y, NULL, 0);
r = new_region(x, y, findplane(x, y), 0);
}
if (!terrain) {
terrain_type *t = get_or_create_terrain("plain");

View File

@ -79,14 +79,20 @@ static void changefaction_write(const trigger * t, struct storage *store)
{
changefaction_data *td = (changefaction_data *)t->data.v;
write_unit_reference(td->unit, store);
write_faction_reference(td->faction, store);
write_faction_reference(td->faction->_alive ? td->faction : NULL, store);
}
static int changefaction_read(trigger * t, struct storage *store)
{
variant var;
changefaction_data *td = (changefaction_data *)t->data.v;
read_reference(&td->unit, store, read_unit_reference, resolve_unit);
read_reference(&td->faction, store, read_faction_reference, resolve_faction);
var = read_faction_reference(store);
if (var.i == 0) {
return AT_READ_FAIL;
}
ur_add(var, &td->faction, resolve_faction);
// read_reference(&td->faction, store, read_faction_reference, resolve_faction);
return AT_READ_OK;
}

View File

@ -82,7 +82,7 @@ static int createunit_handle(trigger * t, void *data)
static void createunit_write(const trigger * t, struct storage *store)
{
createunit_data *td = (createunit_data *)t->data.v;
write_faction_reference(td->f, store);
write_faction_reference(td->f->_alive ? td->f : NULL, store);
write_region_reference(td->r, store);
write_race_reference(td->race, store);
WRITE_INT(store, td->number);
@ -91,21 +91,28 @@ static void createunit_write(const trigger * t, struct storage *store)
static int createunit_read(trigger * t, struct storage *store)
{
createunit_data *td = (createunit_data *)t->data.v;
variant var;
int result = AT_READ_OK;
int uc =
read_reference(&td->f, store, read_faction_reference, resolve_faction);
int rc =
read_reference(&td->r, store, read_region_reference,
var = read_faction_reference(store);
if (var.i > 0) {
td->f = findfaction(var.i);
if (!td->f) {
ur_add(var, &td->f, resolve_faction);
}
}
else {
result = AT_READ_FAIL;
}
// read_reference(&td->f, store, read_faction_reference, resolve_faction);
read_reference(&td->r, store, read_region_reference,
RESOLVE_REGION(global.data_version));
td->race = (const struct race *)read_race_reference(store).v;
if (uc == 0 && rc == 0) {
if (!td->f || !td->r)
return AT_READ_FAIL;
if (!td->race) {
result = AT_READ_FAIL;
}
READ_INT(store, &td->number);
return AT_READ_OK;
return result;
}
trigger_type tt_createunit = {

25
src/util/gamedata.test.c Normal file
View File

@ -0,0 +1,25 @@
#include <platform.h>
#include "gamedata.h"
#include <CuTest.h>
#include <tests.h>
#include <stdio.h>
static void test_gamedata(CuTest * tc)
{
gamedata *data;
data = gamedata_open("test.dat", "wb", 0);
CuAssertPtrNotNull(tc, data);
gamedata_close(data);
data = gamedata_open("test.dat", "rb", 0);
CuAssertPtrNotNull(tc, data);
gamedata_close(data);
CuAssertIntEquals(tc, 0, remove("test.dat"));
}
CuSuite *get_gamedata_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_gamedata);
return suite;
}

View File

@ -2,7 +2,9 @@
#include "password.h"
#include <md5.h>
#include <crypt_blowfish.h>
#include <mtrand.h>
#include <drepper.h>
#include <assert.h>
#include <string.h>
@ -25,73 +27,104 @@
} while (0)
char *password_gensalt(void) {
static char salt[SALTLEN + 1];
char *password_gensalt(char *salt, size_t salt_len) {
size_t buflen = salt_len-1;
char *cp = salt;
int buflen = SALTLEN;
while (buflen) {
unsigned long ul = genrand_int32() & (unsigned long)time(0);
b64_from_24bit((char)(ul & 0xFF), (char)((ul>>8)&0xff), (char)((ul>>16)&0xFF), 4);
b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4);
}
salt[SALTLEN] = 0;
salt[salt_len-1] = 0;
return salt;
}
static const char * password_hash_i(const char * passwd, const char *salt, int algo, char *result, size_t len) {
assert(passwd);
if (!salt) {
salt = password_gensalt();
}
if (algo==PASSWORD_PLAIN) {
_snprintf(result, len, "$0$%s$%s", salt, passwd);
}
else if (algo == PASSWORD_MD5) {
return md5_crypt_r(passwd, salt, result, len);
}
else if (algo == PASSWORD_APACHE_MD5) {
apr_md5_encode(passwd, salt, result, len);
static bool password_is_implemented(int algo) {
return algo == PASSWORD_PLAINTEXT || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5;
}
static const char * password_hash_i(const char * passwd, const char *input, int algo, char *result, size_t len) {
if (algo == PASSWORD_BCRYPT) {
char salt[MAXSALTLEN];
char setting[40];
if (!input) {
input = password_gensalt(salt, MAXSALTLEN);
}
if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) {
return NULL;
}
if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) {
return NULL;
}
return result;
}
else {
return NULL;
else if (algo == PASSWORD_PLAINTEXT) {
_snprintf(result, len, "%s", passwd);
return result;
}
return result;
else if (algo == PASSWORD_NOCRYPT) {
_snprintf(result, len, "$0$%s", passwd);
return result;
}
else if (password_is_implemented(algo)) {
char salt[MAXSALTLEN];
assert(passwd);
if (input) {
const char * dol = strchr(input, '$');
size_t salt_len;
if (dol) {
assert(dol > input && dol[0] == '$');
salt_len = dol - input;
}
else {
salt_len = strlen(input);
}
assert(salt_len < MAXSALTLEN);
memcpy(salt, input, salt_len);
salt[salt_len] = 0;
} else {
input = password_gensalt(salt, sizeof(salt));
}
if (algo == PASSWORD_MD5) {
return md5_crypt_r(passwd, input, result, len);
}
else if (algo == PASSWORD_APACHE_MD5) {
apr_md5_encode(passwd, input, result, len);
return result;
}
}
return NULL;
}
const char * password_hash(const char * passwd, const char * salt, int algo) {
const char * password_encode(const char * passwd, int algo) {
static char result[64]; // TODO: static result buffers are bad mojo!
if (algo < 0) algo = PASSWORD_DEFAULT;
return password_hash_i(passwd, salt, algo, result, sizeof(result));
}
static bool password_is_implemented(int algo) {
return algo==PASSWORD_PLAIN || algo==PASSWORD_MD5 || algo==PASSWORD_APACHE_MD5;
return password_hash_i(passwd, 0, algo, result, sizeof(result));
}
int password_verify(const char * pwhash, const char * passwd) {
char salt[MAXSALTLEN+1];
char hash[64];
size_t len;
int algo;
int algo = PASSWORD_PLAINTEXT;
char *pos;
const char *dol, *result;
const char *result;
assert(passwd);
assert(pwhash);
assert(pwhash[0] == '$');
algo = pwhash[1];
pos = strchr(pwhash+2, '$');
assert(pos && pos[0] == '$');
++pos;
dol = strchr(pos, '$');
assert(dol>pos && dol[0] == '$');
len = dol - pos;
assert(len <= MAXSALTLEN);
strncpy(salt, pos, len);
salt[len] = 0;
result = password_hash_i(passwd, salt, algo, hash, sizeof(hash));
if (pwhash[0] == '$') {
algo = pwhash[1];
}
if (!password_is_implemented(algo)) {
return VERIFY_UNKNOWN;
}
if (algo == PASSWORD_PLAINTEXT) {
return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
} else if (algo == PASSWORD_BCRYPT) {
char sample[200];
_crypt_blowfish_rn(passwd, pwhash, sample, sizeof(sample));
return (strcmp(sample, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL;
}
pos = strchr(pwhash+2, '$');
assert(pos && pos[0] == '$');
pos = strchr(pos, '$')+1;
result = password_hash_i(passwd, pos, algo, hash, sizeof(hash));
if (strcmp(pwhash, result) == 0) {
return VERIFY_OK;
}

View File

@ -1,15 +1,16 @@
#pragma once
#define PASSWORD_PLAIN '0'
#define PASSWORD_PLAINTEXT 0
#define PASSWORD_NOCRYPT '0'
#define PASSWORD_MD5 '1'
#define PASSWORD_BCRYPT '2' // not implemented
#define PASSWORD_APACHE_MD5 'a'
#define PASSWORD_SHA256 '5' // not implemented
#define PASSWORD_SHA512 '6' // not implemented
#define PASSWORD_DEFAULT PASSWORD_APACHE_MD5
#define PASSWORD_DEFAULT PASSWORD_PLAINTEXT
#define VERIFY_OK 0 // password matches hash
#define VERIFY_FAIL 1 // password is wrong
#define VERIFY_UNKNOWN 2 // hashing algorithm not supported
int password_verify(const char *hash, const char *passwd);
const char * password_hash(const char *passwd, const char *salt, int algo);
const char * password_encode(const char *passwd, int algo);

View File

@ -1,25 +1,43 @@
#include <platform.h>
#include <CuTest.h>
#include "password.h"
#include <CuTest.h>
#include <string.h>
static void test_passwords(CuTest *tc) {
const char *hash;
const char *hash, *expect;
hash = password_hash("Hodor", "FqQLkl8g", PASSWORD_APACHE_MD5);
expect = "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660";
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor"));
hash = password_encode("Hodor", PASSWORD_APACHE_MD5);
CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660", hash);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "Hodor"));
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6));
hash = password_hash("jollygood", "ZouUn04i", PASSWORD_MD5);
expect = "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/";
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "jollygood"));
hash = password_encode("jollygood", PASSWORD_MD5);
CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood"));
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3));
hash = password_hash("password", "hodor", PASSWORD_PLAIN);
expect = "password";
hash = password_encode("password", PASSWORD_PLAINTEXT);
CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$0$hodor$password", hash);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "password"));
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(hash, "arseword"));
CuAssertStrEquals(tc, hash, expect);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password"));
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword"));
expect = "$0$password";
hash = password_encode("password", PASSWORD_NOCRYPT);
CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, hash, expect);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password"));
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword"));
expect = "$2y$05$RJ8qAhu.foXyJLdc2eHTLOaK4MDYn3/v4HtOVCq0Plv2yxcrEB7Wm";
CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor"));
hash = password_encode("Hodor", PASSWORD_BCRYPT);
CuAssertPtrNotNull(tc, hash);
CuAssertIntEquals(tc, 0, strncmp(hash, expect, 7));
CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password"));
}

View File

@ -58,6 +58,7 @@ resolve_fun resolver)
void ur_add(variant data, void *ptrptr, resolve_fun fun)
{
assert(ptrptr);
if (ur_list == NULL) {
ur_list = malloc(BLOCKSIZE * sizeof(unresolved));
ur_begin = ur_current = ur_list;
@ -86,6 +87,7 @@ void resolve(void)
ur_list = ur;
continue;
}
assert(ur->ptrptr);
ur->resolve(ur->data, ur->ptrptr);
++ur;
}

Binary file not shown.

View File

@ -1,4 +1,16 @@
ERESSEA 6rLo "4jLm82"
ERESSEA 6rLo "6rLo"
EINHEIT 7Lgf
NACH NW NW
NAECHSTER
ERESSEA w86y "w86y"
EINHEIT uc3u
STIRB "mrqa"
NAECHSTER
ERESSEA ngij "ngij"
EINHEIT iwbz
HELFE w86y ALLES
EINHEIT j536
GRUPPE "Hodor"
HELFE w86y ALLES
HELFE w86y SILBER NICHT
NAECHSTER

View File

@ -37,7 +37,7 @@ SUPP=../share/ubuntu-12_04.supp
SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER"
fi
echo "running $SERVER"
$SERVER -t 184 ../scripts/run-turn.lua
$SERVER -t 184 test-turn.lua
[ -d reports ] || quit 4 "no reports directory created"
CRFILE=185-zvto.cr
for file in $NEWFILES reports/$CRFILE ; do

5
tests/test-turn.lua Normal file
View File

@ -0,0 +1,5 @@
dofile('../scripts/run-turn.lua')
turn = get_turn()
eressea.free_game()
print("trying to read data from " .. turn)
eressea.read_game(turn .. ".dat")

View File

@ -25,7 +25,7 @@ TESTS=../Debug/eressea/test_eressea
SERVER=../Debug/eressea/eressea
if [ -n "$VALGRIND" ]; then
SUPP=../share/ubuntu-12_04.supp
VALGRIND="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no"
VALGRIND="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no"
fi
echo "running $TESTS"
$VALGRIND $TESTS