fix write_faction_reference, check for f->alive in other places

TODO:
1. I added some new TODOs
2. What happens to morale when region owners die?
3. Needs tests
This commit is contained in:
Enno Rehling 2016-02-16 07:30:26 +01:00
parent 4050f994a4
commit 7fe07439f5
29 changed files with 495 additions and 172 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 stpncpy sse3
Memcheck:Cond Memcheck:Cond

View File

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

View File

@ -228,21 +228,11 @@ static void message_faction(battle * b, faction * f, struct message *m)
void message_all(battle * b, message * m) void message_all(battle * b, message * m)
{ {
bfaction *bf; bfaction *bf;
plane *p = rplane(b->region);
watcher *w;
for (bf = b->factions; bf; bf = bf->next) { for (bf = b->factions; bf; bf = bf->next) {
assert(bf->faction); assert(bf->faction);
message_faction(b, bf->faction, m); 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) static void fbattlerecord(battle * b, faction * f, const char *s)

View File

@ -392,7 +392,7 @@ static int tolua_faction_set_password(lua_State * L)
{ {
faction *self = (faction *)tolua_tousertype(L, 1, 0); faction *self = (faction *)tolua_tousertype(L, 1, 0);
const char * passw = tolua_tostring(L, 2, 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; 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) static int tolua_unit_set_group(lua_State * L)
{ {
unit *self = (unit *)tolua_tousertype(L, 1, 0); unit *self = (unit *)tolua_tousertype(L, 1, 0);
int result = join_group(self, tolua_tostring(L, 2, 0)); group *g = join_group(self, tolua_tostring(L, 2, 0));
lua_pushinteger(L, result); lua_pushboolean(L, g!=NULL);
return 1; return 1;
} }

View File

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

View File

@ -4,6 +4,7 @@
#include "alliance.h" #include "alliance.h"
#include <CuTest.h> #include <CuTest.h>
#include <tests.h> #include <tests.h>
#include <quicklist.h>
#include <assert.h> #include <assert.h>
@ -61,9 +62,31 @@ static void test_alliance_join(CuTest *tc) {
test_cleanup(); 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 *get_alliance_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_alliance_dead_faction);
SUITE_ADD_TEST(suite, test_alliance_make); SUITE_ADD_TEST(suite, test_alliance_make);
SUITE_ADD_TEST(suite, test_alliance_join); SUITE_ADD_TEST(suite, test_alliance_join);
return suite; return suite;

View File

@ -252,7 +252,7 @@ faction *addfaction(const char *email, const char *password,
} }
if (!password) password = itoa36(rng_int()); 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)); ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
f->alliance_joindate = turn; f->alliance_joindate = turn;
@ -567,7 +567,8 @@ void faction_setbanner(faction * self, const char *banner)
void faction_setpassword(faction * f, const char *pwhash) void faction_setpassword(faction * f, const char *pwhash)
{ {
assert(pwhash && pwhash[0] == '$'); assert(pwhash);
// && pwhash[0] == '$');
free(f->_password); free(f->_password);
f->_password = _strdup(pwhash); f->_password = _strdup(pwhash);
} }

View File

@ -108,12 +108,12 @@ static void test_addfaction(CuTest *tc) {
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung); CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
CuAssertPtrEquals(tc, (void *)factions, (void *)f); CuAssertPtrEquals(tc, (void *)factions, (void *)f);
CuAssertStrEquals(tc, "test@eressea.de", f->email); 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); CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
CuAssertIntEquals(tc, 1234, f->subscription); CuAssertIntEquals(tc, 1234, f->subscription);
CuAssertIntEquals(tc, 0, f->flags); CuAssertIntEquals(tc, 0, f->flags);
CuAssertIntEquals(tc, 0, f->age); CuAssertIntEquals(tc, 0, f->age);
CuAssertIntEquals(tc, true, faction_alive(f)); CuAssertTrue(tc, faction_alive(f));
CuAssertIntEquals(tc, M_GRAY, f->magiegebiet); CuAssertIntEquals(tc, M_GRAY, f->magiegebiet);
CuAssertIntEquals(tc, turn, f->lastorders); CuAssertIntEquals(tc, turn, f->lastorders);
CuAssertPtrEquals(tc, f, findfaction(f->no)); CuAssertPtrEquals(tc, f, findfaction(f->no));
@ -124,10 +124,10 @@ static void test_check_passwd(CuTest *tc) {
faction *f; faction *f;
f = test_create_faction(0); f = test_create_faction(0);
faction_setpassword(f, password_hash("password", 0, PASSWORD_DEFAULT)); faction_setpassword(f, password_encode("password", PASSWORD_DEFAULT));
CuAssertIntEquals(tc, true, checkpasswd(f, "password")); CuAssertTrue(tc, checkpasswd(f, "password"));
CuAssertIntEquals(tc, false, checkpasswd(f, "assword")); CuAssertTrue(tc, !checkpasswd(f, "assword"));
CuAssertIntEquals(tc, false, checkpasswd(f, "PASSWORD")); CuAssertTrue(tc, !checkpasswd(f, "PASSWORD"));
} }
static void test_get_monsters(CuTest *tc) { 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; group *g = NULL;
@ -192,7 +192,7 @@ bool join_group(unit * u, const char *name)
} }
set_group(u, g); set_group(u, g);
return true; return g;
} }
void write_groups(struct storage *store, const faction * f) void write_groups(struct storage *store, const faction * f)

View File

@ -36,7 +36,7 @@ extern "C" {
} group; } group;
extern struct attrib_type at_group; /* attribute for units assigned to a 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 void set_group(struct unit *u, struct group *g);
extern struct group * get_group(const struct unit *u); extern struct group * get_group(const struct unit *u);
extern void free_group(struct group *g); extern void free_group(struct group *g);

View File

@ -83,8 +83,8 @@ static void test_group(CuTest * tc)
assert(r && f); assert(r && f);
u = test_create_unit(f, r); u = test_create_unit(f, r);
assert(u); assert(u);
CuAssertTrue(tc, join_group(u, "hodor")); CuAssertPtrNotNull(tc, (g = join_group(u, "hodor")));
CuAssertPtrNotNull(tc, (g = get_group(u))); CuAssertPtrEquals(tc, g, get_group(u));
CuAssertStrEquals(tc, "hodor", g->name); CuAssertStrEquals(tc, "hodor", g->name);
CuAssertIntEquals(tc, 1, g->members); CuAssertIntEquals(tc, 1, g->members);
set_group(u, 0); 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); ur_add(id, pp, resolve_plane);
return AT_READ_OK; 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

@ -23,6 +23,7 @@ extern "C" {
#endif #endif
struct region; struct region;
struct faction;
struct plane; struct plane;
struct storage; struct storage;
@ -43,15 +44,8 @@ extern "C" {
#define PFL_NOMONSTERS 16384 /* no monster randenc */ #define PFL_NOMONSTERS 16384 /* no monster randenc */
#define PFL_SEESPECIAL 32768 /* far seeing */ #define PFL_SEESPECIAL 32768 /* far seeing */
typedef struct watcher {
struct watcher *next;
struct faction *faction;
unsigned char mode;
} watcher;
typedef struct plane { typedef struct plane {
struct plane *next; struct plane *next;
struct watcher *watchers;
int id; int id;
char *name; char *name;
int minx, maxx, miny, maxy; int minx, maxx, miny, maxy;
@ -76,7 +70,6 @@ extern "C" {
struct plane *get_homeplane(void); struct plane *get_homeplane(void);
extern int rel_to_abs(const struct plane *pl, const struct faction *f, extern int rel_to_abs(const struct plane *pl, const struct faction *f,
int rel, unsigned char index); 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 void write_plane_reference(const plane * p, struct storage *store);
extern int read_plane_reference(plane ** pp, struct storage *store); extern int read_plane_reference(plane ** pp, struct storage *store);
extern int plane_width(const plane * pl); extern int plane_width(const plane * pl);

View File

@ -544,7 +544,8 @@ static void write_owner(struct gamedata *data, region_owner * owner)
write_faction_reference((f && f->_alive) ? f : NULL, data->store); write_faction_reference((f && f->_alive) ? f : NULL, data->store);
// TODO: check that destroyfaction does the right thing. // TODO: check that destroyfaction does the right thing.
// TODO: What happens to morale when the owner dies? // TODO: What happens to morale when the owner dies?
write_faction_reference(owner->owner, data->store); f = owner->owner;
write_faction_reference((f && f->_alive) ? f : NULL, data->store);
} }
else { else {
WRITE_INT(data->store, -1); WRITE_INT(data->store, -1);
@ -766,6 +767,7 @@ void write_unit(struct gamedata *data, const unit * u)
const race *irace = u_irace(u); const race *irace = u_irace(u);
write_unit_reference(u, data->store); write_unit_reference(u, data->store);
assert(u->faction->_alive);
write_faction_reference(u->faction, data->store); write_faction_reference(u->faction, data->store);
WRITE_STR(data->store, u->_name); WRITE_STR(data->store, u->_name);
WRITE_STR(data->store, u->display ? u->display : ""); WRITE_STR(data->store, u->display ? u->display : "");
@ -1159,6 +1161,58 @@ void write_spellbook(const struct spellbook *book, struct storage *store)
WRITE_TOK(store, "end"); 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. /** Reads a faction from a file.
* This function requires no context, can be called in any state. The * This function requires no context, can be called in any state. The
* faction may not already exist, however. * faction may not already exist, however.
@ -1225,8 +1279,7 @@ faction *readfaction(struct gamedata * data)
set_email(&f->email, ""); set_email(&f->email, "");
} }
READ_STR(data->store, name, sizeof(name)); read_password(data, f);
faction_setpassword(f, (data->version > CRYPT_VERSION) ? name : password_hash(name, 0, PASSWORD_DEFAULT));
if (data->version < NOOVERRIDE_VERSION) { if (data->version < NOOVERRIDE_VERSION) {
READ_STR(data->store, 0, 0); READ_STR(data->store, 0, 0);
} }
@ -1313,6 +1366,7 @@ void writefaction(struct gamedata *data, const faction * f)
ally *sf; ally *sf;
ursprung *ur; ursprung *ur;
assert(f->_alive);
write_faction_reference(f, data->store); write_faction_reference(f, data->store);
WRITE_INT(data->store, f->subscription); WRITE_INT(data->store, f->subscription);
#if RELEASE_VERSION >= SPELL_LEVEL_VERSION #if RELEASE_VERSION >= SPELL_LEVEL_VERSION
@ -1333,7 +1387,7 @@ void writefaction(struct gamedata *data, const faction * f)
WRITE_STR(data->store, f->name); WRITE_STR(data->store, f->name);
WRITE_STR(data->store, f->banner); WRITE_STR(data->store, f->banner);
WRITE_STR(data->store, f->email); 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_TOK(data->store, locale_name(f->locale));
WRITE_INT(data->store, f->lastorders); WRITE_INT(data->store, f->lastorders);
WRITE_INT(data->store, f->age); WRITE_INT(data->store, f->age);
@ -1384,32 +1438,6 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) {
return 0; return 0;
} }
void write_watchers(storage *store, plane *pl) {
watcher *w = pl->watchers;
while (w) {
if (w->faction && w->faction->_alive) {
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 */
}
void read_watchers(storage *store, plane *pl) {
variant fno = read_faction_reference(store);
while (fno.i) {
int n;
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);
}
}
int readgame(const char *filename, bool backup) int readgame(const char *filename, bool backup)
{ {
int n, p, nread; int n, p, nread;
@ -1528,7 +1556,13 @@ int readgame(const char *filename, bool backup)
} }
} }
else { else {
read_watchers(&store, pl); /* 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); a_read(&store, &pl->attribs, pl);
if (pl->id != 1094969858) { // Regatta if (pl->id != 1094969858) { // Regatta
@ -1815,7 +1849,9 @@ int writegame(const char *filename)
WRITE_INT(&store, pl->miny); WRITE_INT(&store, pl->miny);
WRITE_INT(&store, pl->maxy); WRITE_INT(&store, pl->maxy);
WRITE_INT(&store, pl->flags); WRITE_INT(&store, pl->flags);
write_watchers(&store, pl); #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); a_write(&store, pl->attribs, pl);
WRITE_SECTION(&store); WRITE_SECTION(&store);
} }

View File

@ -83,6 +83,9 @@ extern "C" {
struct gamedata *gamedata_open(const char *filename, const char *mode); struct gamedata *gamedata_open(const char *filename, const char *mode);
void gamedata_close(struct gamedata *data); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -3,11 +3,25 @@
#include "save.h" #include "save.h"
#include "unit.h" #include "unit.h"
#include "group.h"
#include "ally.h"
#include "faction.h" #include "faction.h"
#include "plane.h"
#include "region.h"
#include "version.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 <CuTest.h>
#include <tests.h> #include <tests.h>
#include <storage.h>
#include <stdio.h> #include <stdio.h>
static void test_readwrite_data(CuTest * tc) static void test_readwrite_data(CuTest * tc)
@ -42,15 +56,17 @@ static void test_readwrite_unit(CuTest * tc)
u = test_create_unit(f, r); u = test_create_unit(f, r);
join_path(datapath(), filename, path, sizeof(path)); join_path(datapath(), filename, path, sizeof(path));
data = gamedata_open(path, "w"); data = gamedata_open(path, "wb");
CuAssertPtrNotNull(tc, data); // TODO: intermittent test CuAssertPtrNotNull(tc, data);
write_unit(data, u); write_unit(data, u);
gamedata_close(data); gamedata_close(data);
free_gamedata(); free_gamedata();
f = test_create_faction(0); f = test_create_faction(0);
renumber_faction(f, fno); renumber_faction(f, fno);
data = gamedata_open(path, "r"); data = gamedata_open(path, "rb");
CuAssertPtrNotNull(tc, data);
u = read_unit(data); u = read_unit(data);
gamedata_close(data); gamedata_close(data);
@ -61,10 +77,189 @@ static void test_readwrite_unit(CuTest * tc)
test_cleanup(); 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 *get_save_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_readwrite_data); SUITE_ADD_TEST(suite, test_readwrite_data);
SUITE_ADD_TEST(suite, test_readwrite_unit); 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; return suite;
} }

View File

@ -33,9 +33,10 @@
#define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */ #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 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 ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */
#define CRYPT_VERSION 351 /* passwords are encrypted */ #define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */
#define RELEASE_VERSION CRYPT_VERSION /* current datafile */ #define CRYPT_VERSION 352 /* passwords are encrypted */
#define RELEASE_VERSION ATTRIBOWNER_VERSION /* current datafile */
#define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #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 */ #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); cmistake(u, ord, 283, MSG_EVENT);
strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf)); 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", ADDMSG(&u->faction->msgs, msg_message("changepasswd",
"value", pwbuf)); "value", pwbuf));
return 0; return 0;

View File

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

View File

@ -5,6 +5,7 @@
#include "prefix.h" #include "prefix.h"
#include <kernel/config.h> #include <kernel/config.h>
#include <kernel/plane.h>
#include <kernel/region.h> #include <kernel/region.h>
#include <kernel/terrain.h> #include <kernel/terrain.h>
#include <kernel/item.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); region *r = findregion(x, y);
if (!r) { if (!r) {
r = new_region(x, y, NULL, 0); r = new_region(x, y, findplane(x, y), 0);
} }
if (!terrain) { if (!terrain) {
terrain_type *t = get_or_create_terrain("plain"); 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; changefaction_data *td = (changefaction_data *)t->data.v;
write_unit_reference(td->unit, store); 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) static int changefaction_read(trigger * t, struct storage *store)
{ {
variant var;
changefaction_data *td = (changefaction_data *)t->data.v; changefaction_data *td = (changefaction_data *)t->data.v;
read_reference(&td->unit, store, read_unit_reference, resolve_unit); 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; 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) static void createunit_write(const trigger * t, struct storage *store)
{ {
createunit_data *td = (createunit_data *)t->data.v; 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_region_reference(td->r, store);
write_race_reference(td->race, store); write_race_reference(td->race, store);
WRITE_INT(store, td->number); WRITE_INT(store, td->number);
@ -91,21 +91,27 @@ static void createunit_write(const trigger * t, struct storage *store)
static int createunit_read(trigger * t, struct storage *store) static int createunit_read(trigger * t, struct storage *store)
{ {
createunit_data *td = (createunit_data *)t->data.v; createunit_data *td = (createunit_data *)t->data.v;
variant var;
int result = AT_READ_OK;
int uc = var = read_faction_reference(store);
read_reference(&td->f, store, read_faction_reference, resolve_faction); if (var.i > 0) {
int rc = td->f = findfaction(var.i);
if (!td->f) {
ur_add(var, &td->f, resolve_faction);
}
}
else {
result = AT_READ_FAIL;
}
read_reference(&td->r, store, read_region_reference, read_reference(&td->r, store, read_region_reference,
RESOLVE_REGION(global.data_version)); RESOLVE_REGION(global.data_version));
td->race = (const struct race *)read_race_reference(store).v; td->race = (const struct race *)read_race_reference(store).v;
if (!td->race) {
if (uc == 0 && rc == 0) { result = AT_READ_FAIL;
if (!td->f || !td->r)
return AT_READ_FAIL;
} }
READ_INT(store, &td->number); READ_INT(store, &td->number);
return result;
return AT_READ_OK;
} }
trigger_type tt_createunit = { 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 "password.h"
#include <md5.h> #include <md5.h>
#include <crypt_blowfish.h>
#include <mtrand.h> #include <mtrand.h>
#include <drepper.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -25,73 +27,104 @@
} while (0) } while (0)
char *password_gensalt(void) { char *password_gensalt(char *salt, size_t salt_len) {
static char salt[SALTLEN + 1]; size_t buflen = salt_len-1;
char *cp = salt; char *cp = salt;
int buflen = SALTLEN;
while (buflen) { while (buflen) {
unsigned long ul = genrand_int32() & (unsigned long)time(0); 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; return salt;
} }
static const char * password_hash_i(const char * passwd, const char *salt, int algo, char *result, size_t len) { static bool password_is_implemented(int algo) {
assert(passwd); return algo == PASSWORD_PLAINTEXT || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5;
if (!salt) {
salt = password_gensalt();
} }
if (algo==PASSWORD_PLAIN) {
_snprintf(result, len, "$0$%s$%s", salt, passwd); 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);
} }
else if (algo == PASSWORD_MD5) { if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) {
return md5_crypt_r(passwd, salt, result, len); return NULL;
} }
else if (algo == PASSWORD_APACHE_MD5) { if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) {
apr_md5_encode(passwd, salt, result, len);
return result;
}
else {
return NULL; return NULL;
} }
return result; return result;
} }
else if (algo == PASSWORD_PLAINTEXT) {
const char * password_hash(const char * passwd, const char * salt, int algo) { _snprintf(result, len, "%s", passwd);
static char result[64]; // TODO: static result buffers are bad mojo! return result;
if (algo < 0) algo = PASSWORD_DEFAULT; }
return password_hash_i(passwd, salt, algo, result, sizeof(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;
} }
static bool password_is_implemented(int algo) { const char * password_encode(const char * passwd, int algo) {
return algo==PASSWORD_PLAIN || algo==PASSWORD_MD5 || algo==PASSWORD_APACHE_MD5; static char result[64]; // TODO: static result buffers are bad mojo!
if (algo < 0) algo = PASSWORD_DEFAULT;
return password_hash_i(passwd, 0, algo, result, sizeof(result));
} }
int password_verify(const char * pwhash, const char * passwd) { int password_verify(const char * pwhash, const char * passwd) {
char salt[MAXSALTLEN+1];
char hash[64]; char hash[64];
size_t len; int algo = PASSWORD_PLAINTEXT;
int algo;
char *pos; char *pos;
const char *dol, *result; const char *result;
assert(passwd); assert(passwd);
assert(pwhash); assert(pwhash);
assert(pwhash[0] == '$'); if (pwhash[0] == '$') {
algo = pwhash[1]; 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 (!password_is_implemented(algo)) { if (!password_is_implemented(algo)) {
return VERIFY_UNKNOWN; 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) { if (strcmp(pwhash, result) == 0) {
return VERIFY_OK; return VERIFY_OK;
} }

View File

@ -1,15 +1,16 @@
#pragma once #pragma once
#define PASSWORD_PLAIN '0' #define PASSWORD_PLAINTEXT 0
#define PASSWORD_NOCRYPT '0'
#define PASSWORD_MD5 '1' #define PASSWORD_MD5 '1'
#define PASSWORD_BCRYPT '2' // not implemented #define PASSWORD_BCRYPT '2' // not implemented
#define PASSWORD_APACHE_MD5 'a' #define PASSWORD_APACHE_MD5 'a'
#define PASSWORD_SHA256 '5' // not implemented #define PASSWORD_SHA256 '5' // not implemented
#define PASSWORD_SHA512 '6' // 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_OK 0 // password matches hash
#define VERIFY_FAIL 1 // password is wrong #define VERIFY_FAIL 1 // password is wrong
#define VERIFY_UNKNOWN 2 // hashing algorithm not supported #define VERIFY_UNKNOWN 2 // hashing algorithm not supported
int password_verify(const char *hash, const char *passwd); 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 <platform.h>
#include <CuTest.h>
#include "password.h" #include "password.h"
#include <CuTest.h>
#include <string.h>
static void test_passwords(CuTest *tc) { 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); CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660", hash); CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6));
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "Hodor"));
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); CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/", hash); CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3));
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "jollygood"));
hash = password_hash("password", "hodor", PASSWORD_PLAIN); expect = "password";
hash = password_encode("password", PASSWORD_PLAINTEXT);
CuAssertPtrNotNull(tc, hash); CuAssertPtrNotNull(tc, hash);
CuAssertStrEquals(tc, "$0$hodor$password", hash); CuAssertStrEquals(tc, hash, expect);
CuAssertIntEquals(tc, VERIFY_OK, password_verify(hash, "password")); CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password"));
CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(hash, "arseword")); 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")); CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password"));
} }

View File

@ -25,7 +25,7 @@ TESTS=../Debug/eressea/test_eressea
SERVER=../Debug/eressea/eressea SERVER=../Debug/eressea/eressea
if [ -n "$VALGRIND" ]; then if [ -n "$VALGRIND" ]; then
SUPP=../share/ubuntu-12_04.supp 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 fi
echo "running $TESTS" echo "running $TESTS"
$VALGRIND $TESTS $VALGRIND $TESTS