Crossfire Server, Trunk
account.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
50 #include <set>
51 #include <string>
52 
54 static std::set<std::string> accounts_logged_in = std::set<std::string>();
55 
56 #include "global.h"
57 
58 #include <ctype.h>
59 #include <stdlib.h>
60 #include <string.h>
61 
62 #include "object.h"
63 #include "sproto.h"
64 
65 #include "output_file.h"
66 
68 #define NUM_ACCOUNT_FIELDS 6
69 
82  char *name;
83  char *password;
84  time_t last_login;
87  char **character_names;
89  time_t created;
90 };
91 
95 static std::vector<account_struct *> accounts;
96 
103 static int accounts_loaded = 0;
104 
109 #define ACCOUNT_FILE "accounts"
110 
117  if (count >= account->allocated_characters) { // The list is NULL-terminated
118  const int pa = account->allocated_characters;
119  account->allocated_characters = count + 1;
120  account->character_names = static_cast<char **>(realloc(account->character_names, account->allocated_characters * sizeof(account->character_names[0])));
121  if (!account->character_names) {
122  LOG(llevError, "Unable to allocate %d characters names!", account->allocated_characters);
124  }
125  for (int i = pa; i < account->allocated_characters; i++) {
126  account->character_names[i] = NULL;
127  }
128  }
129 }
130 
137  account_struct *ac = (account_struct *)calloc(1, sizeof(account_struct));
138  if (!ac) {
139  LOG(llevError, "Unable to allocate an account_struct!\n");
141  }
142  ac->last_login = time(NULL);
143  ac->created = ac->last_login;
145  return ac;
146 }
147 
153 void accounts_clear(void) {
154  accounts.clear();
155  accounts_loaded = 0;
156 }
157 
162 void accounts_load(void) {
163  char fname[MAX_BUF], *buf;
164  int fields=0;
165  BufferReader *br;
166 
167  if (!accounts.empty()) {
168  LOG(llevError, "account_load_entries(): Called when accounts has been set.\n");
169  return;
170  }
171  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
172  if ((br = bufferreader_init_from_file(NULL, fname, "Warning: Unable to open %s [%s]\n", llevInfo)) == NULL) {
173  return;
174  }
175 
176  while ((buf = bufferreader_next_line(br))) {
177  char *tmp[NUM_ACCOUNT_FIELDS], *cp;
178  int i;
179 
180  /* Ignore any comment lines */
181  if (buf[0] == '#') continue;
182 
183  fields = split_string(buf, tmp, NUM_ACCOUNT_FIELDS, ':');
184 
186  ac->name = strdup_local(tmp[0]);
187  ac->password = strdup_local(tmp[1]);
188  ac->last_login = strtoul(tmp[2], (char**)NULL, 10);
189 
190  /* While probably no one was using this code before this
191  * field was added, this provides a nice example of handling
192  * additional fields.
193  */
194  if (fields>4) ac->created = strtoul(tmp[4], (char**)NULL, 10);
195  else
196  ac->created = ac->last_login;
197 
198  /* If this is a blank field, nothing to do */
199  if (tmp[3][0]) {
200  /* count up how many semicolons - this is the character
201  * separator. We start at one, because these are separators,
202  * so there will be one more name than separators.
203  */
204  ac->num_characters=1;
205  for (cp = tmp[3]; *cp != '\0'; cp++) {
206  if (*cp == ';') ac->num_characters++;
207  }
208  ensure_available_characters(ac, ac->num_characters);
209 
210  split_string(tmp[3], ac->character_names, ac->num_characters, ';');
211 
212  /* The string data that the names are stored in is currently temporary data
213  * that will go away, so we need to allocate some permanent data now */
214  for (i=0; i<ac->num_characters; i++) {
215  ac->character_names[i] = strdup_local(ac->character_names[i]);
216  }
217  }
218 
219  accounts.push_back(ac);
220  }
221 
223  accounts_loaded = 1;
224 }
225 
235 static void account_write_entry(FILE *fp, account_struct *ac)
236 {
237  int i;
238 
239  fprintf(fp,"%s:%s:%u:", ac->name, ac->password, (uint32_t)ac->last_login);
240  for (i=0; i<ac->num_characters; i++) {
241  if (i != 0)
242  fprintf(fp,";%s", ac->character_names[i]);
243  else
244  fprintf(fp,"%s", ac->character_names[i]);
245  }
246  fprintf(fp,":%u:\n", (uint32_t) ac->created);
247 }
248 
249 
256 void accounts_save(void)
257 {
258  char fname[MAX_BUF];
259  FILE *fp;
260  OutputFile of;
261 
262  if (accounts_loaded == 0)
263  return;
264 
265  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
266 
267  fp = of_open(&of, fname);
268  if (fp == NULL)
269  return;
270 
271  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
272  "# only read when the server starts, and any changes will be overwritten when\n"
273  "# the server exits.\n");
274  fprintf(fp, "# Format:\n");
275  fprintf(fp, "# Account name:Password:Account last used:Characters (semicolon separated):created:expansion\n");
276  for (auto ac : accounts) {
277  /* Don't write out accounts with no characters associated unless the
278  * account is at least a day old.
279  */
280  // 86400 seconds in a day, so no reason to make the game have to recalculate this all the time
281  // SilverNexus 2014-06-12
282  if (ac->num_characters || (ac->created > (time(NULL) - 86400)))
283  account_write_entry(fp, ac);
284  }
285  of_close(&of);
286 }
287 
297 const char *account_exists(const char *account_name)
298 {
299  for (auto ac : accounts) {
300  if (!strcasecmp(ac->name, account_name)) return ac->name;
301  }
302  return NULL;
303 }
304 
319 int account_login(const char *account_name, const char *account_password) {
320  for (auto ac : accounts) {
321  /* Look for a matching account name and check the password. */
322  if (!strcasecmp(ac->name, account_name)) {
323  if (check_password(account_password, ac->password)) {
324  ac->last_login = time(NULL);
325  accounts_logged_in.insert(account_name);
326  return 1;
327  } else {
328  return 0;
329  }
330  }
331  }
332  return 0;
333 }
334 
338 void account_logout(const char *account_name) {
339  accounts_logged_in.erase(account_name);
340 }
341 
361 int account_check_string(const char *str)
362 {
363  const char *cp = str;
364 
365  /* Require first character to be letter or number */
366  if (!isalnum(*str)) return 1;
367  for (; *str != '\0'; ++str) {
368  if (!isprint(*str)) return 1;
369  switch (*str){
370  case ':':
371  case ';':
372  case '/':
373  case '\'':
374  case '[':
375  return 1;
376  }
377  }
378  /* Don't allow space characters at end of string. */
379  if (isspace(*(str-1))) return 1;
380  if ((str - cp) > MAX_NAME) return 2;
381  return 0;
382 }
383 
384 
400 int account_new(const char *account_name, const char *account_password) {
402 
403  // Check password for invalid characters because newhash() may just
404  // return the string in plaintext.
406  return 1;
407 
408  if (account_exists(account_name)) return 2;
409 
410  ac = account_alloc();
411  ac->name = strdup_local(account_name);
412  ac->password = strdup_local(newhash(account_password));
413 
414  /* We put this at the top of the list. This means recent accounts will be at
415  * the top of the file, which is likely a good thing.
416  * We don't do a save right now. When the player associates a character with
417  * the account, we will save them - until that point, not too much reason
418  * to save this out. Note it is still possible for this to get saved out if
419  * another player does something that forces writing out of the accounts file.
420  */
421  accounts.insert(accounts.begin(), ac);
422 
423  /* mark that accounts should be saved through accounts_save(). */
424  accounts_loaded = 1;
425 
426  return 0;
427 }
428 
445 int account_link(const char *account_name, const char *player_name) {
446  for (auto ac : accounts) {
447  if (!strcasecmp(ac->name, account_name)) {
448  ensure_available_characters(ac, ac->num_characters + 1);
449  ac->character_names[ac->num_characters] = strdup_local(player_name);
450  ac->num_characters++;
451  return 0;
452  }
453  }
454  return 1;
455 }
456 
475 int account_remove_player(const char *account_name, const char *player_name) {
476  int i, match=0;
477 
478  if (account_name == NULL)
479  return 0;
480 
481  for (auto ac : accounts) {
482  if (!strcasecmp(ac->name, account_name)) {
483  /* Try to find the character name. Once we find it, we set match, and
484  * then move the remain character names down by one. The array is
485  * always null terminated, so this also makes sure we copy the null down.
486  */
487  for (i=0; i<ac->num_characters; i++) {
488  if (!strcmp(ac->character_names[i], player_name)) {
489  free(ac->character_names[i]);
490  match=1;
491  }
492  if (match == 1) {
493  ac->character_names[i] = ac->character_names[i+1];
494  }
495  }
496 
497  if (match) {
498  ac->num_characters--;
499  return 0;
500  }
501  /* Otherwise, did not find player name */
502  return 2;
503  }
504  }
505  return 1;
506 }
507 
508 
520 char **account_get_players_for_account(const char *account_name)
521 {
522  for (auto ac : accounts) {
523  if (!strcasecmp(ac->name, account_name)) return ac->character_names;
524  }
525  return NULL;
526 }
527 
534 static int char_in_list(const char *name, const std::vector<Account_Char *> chars) {
535  for (auto ch : chars) {
536  if (strcmp(ch->name, name) == 0) {
537  return 1;
538  }
539  }
540  return 0;
541 }
542 
551 linked_char *account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count) {
552  linked_char *ret = NULL;
553 
554  for (auto ac : accounts) {
555  if (!strcasecmp(ac->name, account_name)) {
556  for (int i = 0; i < ac->num_characters; i++) {
557  if (!char_in_list(ac->character_names[i], chars->chars)) {
558  linked_char *lc = (linked_char *)calloc(1, sizeof(linked_char));
559  lc->next = ret;
560  lc->name = ac->character_names[i];
561  ret = lc;
562  (*count)++;
563  }
564  }
565  return ret;
566  }
567  }
568  return NULL;
569 }
570 
580 const char *account_get_account_for_char(const char *charname)
581 {
582  int i;
583 
584  for (auto ac : accounts) {
585  for (i=0; i<ac->num_characters; i++) {
586  if (!strcmp(ac->character_names[i], charname)) {
587  return ac->name;
588  }
589  }
590  }
591  return NULL;
592 
593 }
594 
605 int account_is_logged_in(const char *name) {
606  return accounts_logged_in.find(name) != accounts_logged_in.end();
607 }
608 
628 int account_change_password(const char *account_name,
629  const char *current_password, const char *new_password) {
630 
631  // Check password for invalid characters as in account_new().
632  if (account_check_string(account_name) ||
633  (current_password != NULL && account_check_string(current_password)) ||
634  account_check_string(new_password)) {
635  return 1;
636  }
637 
638  // Iterate through accounts list until a matching name is found.
639  for (auto ac : accounts) {
640  if (!strcasecmp(ac->name, account_name)) {
641  // Return an error if the current password does not match.
642  if (current_password != NULL && !check_password(current_password, ac->password)) {
643  return 3;
644  }
645 
646  free(ac->password);
647  ac->password = strdup_local(newhash(new_password));
648 
649  return 0;
650  }
651  }
652 
653  return 2;
654 }
output_file.h
global.h
account_struct::name
char * name
Definition: account.cpp:82
account_get_players_for_account
char ** account_get_players_for_account(const char *account_name)
Definition: account.cpp:520
settings
struct Settings settings
Definition: init.cpp:139
account_logout
void account_logout(const char *account_name)
Definition: account.cpp:338
account_struct
Definition: account.cpp:81
account_alloc
static account_struct * account_alloc()
Definition: account.cpp:136
guildjoin.charname
def charname
Definition: guildjoin.py:45
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
strdup_local
#define strdup_local
Definition: compat.h:29
newhash
char const * newhash(char const *password)
Definition: server.cpp:101
account_get_additional_chars
linked_char * account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count)
Definition: account.cpp:551
accounts_save
void accounts_save(void)
Definition: account.cpp:256
account_struct::created
time_t created
Definition: account.cpp:89
bufferreader_destroy
void bufferreader_destroy(BufferReader *br)
Definition: bufferreader.cpp:40
account_exists
const char * account_exists(const char *account_name)
Definition: account.cpp:297
Ice.tmp
int tmp
Definition: Ice.py:207
accounts_loaded
static int accounts_loaded
Definition: account.cpp:103
curse_on_apply.ac
ac
Definition: curse_on_apply.py:4
account_struct::num_characters
int num_characters
Definition: account.cpp:85
account_remove_player
int account_remove_player(const char *account_name, const char *player_name)
Definition: account.cpp:475
accounts
static std::vector< account_struct * > accounts
Definition: account.cpp:95
char_in_list
static int char_in_list(const char *name, const std::vector< Account_Char * > chars)
Definition: account.cpp:534
buf
StringBuffer * buf
Definition: readable.cpp:1565
linked_char
Definition: global.h:96
Account_Chars::chars
std::vector< Account_Char * > chars
Definition: account_char.h:30
accounts_clear
void accounts_clear(void)
Definition: account.cpp:153
account_password
void account_password(char *buf, int len, socket_struct *ns)
Definition: request.cpp:3044
bufferreader_init_from_file
BufferReader * bufferreader_init_from_file(BufferReader *br, const char *filepath, const char *failureMessage, LogLevel failureLevel)
Definition: bufferreader.cpp:65
linked_char::name
const char * name
Definition: global.h:97
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:473
account_struct::last_login
time_t last_login
Definition: account.cpp:84
NUM_ACCOUNT_FIELDS
#define NUM_ACCOUNT_FIELDS
Definition: account.cpp:68
make_face_from_files.str
str
Definition: make_face_from_files.py:30
linked_char::next
struct linked_char * next
Definition: global.h:98
MAX_NAME
#define MAX_NAME
Definition: define.h:41
disinfect.count
int count
Definition: disinfect.py:7
push.match
bool match
Definition: push.py:61
sproto.h
ACCOUNT_FILE
#define ACCOUNT_FILE
Definition: account.cpp:109
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:590
MAX_BUF
#define MAX_BUF
Definition: define.h:35
ensure_available_characters
static void ensure_available_characters(account_struct *account, int count)
Definition: account.cpp:116
llevInfo
@ llevInfo
Definition: logger.h:12
account_struct::allocated_characters
int allocated_characters
Definition: account.cpp:86
accounts_logged_in
static std::set< std::string > accounts_logged_in
Definition: account.cpp:54
strcasecmp
int strcasecmp(const char *s1, const char *s2)
Account_Chars
Definition: account_char.h:27
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
Definition: account.cpp:580
check_password
bool check_password(const char *typed, const char *crypted)
Definition: server.cpp:114
account_is_logged_in
int account_is_logged_in(const char *name)
Definition: account.cpp:605
account_struct::character_names
char ** character_names
Definition: account.cpp:87
account_change_password
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
Definition: account.cpp:628
account_write_entry
static void account_write_entry(FILE *fp, account_struct *ac)
Definition: account.cpp:235
account_link
int account_link(const char *account_name, const char *player_name)
Definition: account.cpp:445
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
account_login
int account_login(const char *account_name, const char *account_password)
Definition: account.cpp:319
BufferReader
Definition: bufferreader.cpp:21
accounts_load
void accounts_load(void)
Definition: account.cpp:162
object.h
account_check_string
int account_check_string(const char *str)
Definition: account.cpp:361
account_new
int account_new(const char *account_name, const char *account_password)
Definition: account.cpp:400
give.name
name
Definition: give.py:27
account_struct::password
char * password
Definition: account.cpp:83
OutputFile
Definition: output_file.h:41
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.cpp:102
Settings::localdir
const char * localdir
Definition: global.h:249