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 
81 typedef struct account_struct {
82  char *name;
83  char *password;
84  time_t last_login;
87  char **character_names;
89  time_t created;
90  struct account_struct *next;
92 
96 static account_struct *accounts=NULL;
97 
104 static int accounts_loaded = 0;
105 
110 #define ACCOUNT_FILE "accounts"
111 
118  if (count >= account->allocated_characters) { // The list is NULL-terminated
119  const int pa = account->allocated_characters;
120  account->allocated_characters = count + 1;
121  account->character_names = static_cast<char **>(realloc(account->character_names, account->allocated_characters * sizeof(account->character_names[0])));
122  if (!account->character_names) {
123  LOG(llevError, "Unable to allocate %d characters names!", account->allocated_characters);
125  }
126  for (int i = pa; i < account->allocated_characters; i++) {
127  account->character_names[i] = NULL;
128  }
129  }
130 }
131 
138  account_struct *ac = (account_struct *)calloc(1, sizeof(account_struct));
139  if (!ac) {
140  LOG(llevError, "Unable to allocate an account_struct!\n");
142  }
143  ac->last_login = time(NULL);
144  ac->created = ac->last_login;
146  return ac;
147 }
148 
154 void accounts_clear(void) {
155  accounts = NULL;
156  accounts_loaded = 0;
157 }
158 
163 void accounts_load(void) {
164  char fname[MAX_BUF], *buf;
165  account_struct *ac, *last=NULL;
166  int fields=0;
167  BufferReader *br;
168 
169  if (accounts != NULL) {
170  LOG(llevError, "account_load_entries(): Called when accounts has been set.\n");
171  return;
172  }
173  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
174  if ((br = bufferreader_init_from_file(NULL, fname, "Warning: Unable to open %s [%s]\n", llevInfo)) == NULL) {
175  return;
176  }
177 
178  while ((buf = bufferreader_next_line(br))) {
179  char *tmp[NUM_ACCOUNT_FIELDS], *cp;
180  int i;
181 
182  /* Ignore any comment lines */
183  if (buf[0] == '#') continue;
184 
185  fields = split_string(buf, tmp, NUM_ACCOUNT_FIELDS, ':');
186 
187  ac = account_alloc();
188  ac->name = strdup_local(tmp[0]);
189  ac->password = strdup_local(tmp[1]);
190  ac->last_login = strtoul(tmp[2], (char**)NULL, 10);
191 
192  /* While probably no one was using this code before this
193  * field was added, this provides a nice example of handling
194  * additional fields.
195  */
196  if (fields>4) ac->created = strtoul(tmp[4], (char**)NULL, 10);
197  else
198  ac->created = ac->last_login;
199 
200  /* If this is a blank field, nothing to do */
201  if (tmp[3][0]) {
202  /* count up how many semicolons - this is the character
203  * seperator. We start at one, because these are seperators,
204  * so there will be one more name than seperators.
205  */
206  ac->num_characters=1;
207  for (cp = tmp[3]; *cp != '\0'; cp++) {
208  if (*cp == ';') ac->num_characters++;
209  }
210  ensure_available_characters(ac, ac->num_characters);
211 
212  split_string(tmp[3], ac->character_names, ac->num_characters, ';');
213 
214  /* The string data that the names are stored in is currently temporary data
215  * that will go away, so we need to allocate some permanent data now */
216  for (i=0; i<ac->num_characters; i++) {
217  ac->character_names[i] = strdup_local(ac->character_names[i]);
218  }
219  }
220 
221  /* We tack on to the end of the list - in this way,
222  * the order of the file remains the same.
223  */
224  if (last)
225  last->next = ac;
226  else
227  accounts = ac;
228  last = ac;
229 
230  }
231 
233  accounts_loaded = 1;
234 }
235 
245 static void account_write_entry(FILE *fp, account_struct *ac)
246 {
247  int i;
248 
249  fprintf(fp,"%s:%s:%u:", ac->name, ac->password, (uint32_t)ac->last_login);
250  for (i=0; i<ac->num_characters; i++) {
251  if (i != 0)
252  fprintf(fp,";%s", ac->character_names[i]);
253  else
254  fprintf(fp,"%s", ac->character_names[i]);
255  }
256  fprintf(fp,":%u:\n", (uint32_t) ac->created);
257 }
258 
259 
266 void accounts_save(void)
267 {
268  char fname[MAX_BUF];
269  FILE *fp;
270  OutputFile of;
272 
273  if (accounts_loaded == 0)
274  return;
275 
276  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
277 
278  fp = of_open(&of, fname);
279  if (fp == NULL)
280  return;
281 
282  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
283  "# only read when the server starts, and any changes will be overwritten when\n"
284  "# the server exits.\n");
285  fprintf(fp, "# Format:\n");
286  fprintf(fp, "# Account name:Password:Account last used:Characters (semicolon separated):created:expansion\n");
287  for (ac=accounts; ac; ac=ac->next) {
288  /* Don't write out accounts with no characters associated unless the
289  * account is at least a day old.
290  */
291  // 86400 seconds in a day, so no reason to make the game have to recalculate this all the time
292  // SilverNexus 2014-06-12
293  if (ac->num_characters || (ac->created > (time(NULL) - 86400)))
294  account_write_entry(fp, ac);
295  }
296  of_close(&of);
297 }
298 
308 const char *account_exists(const char *account_name)
309 {
311 
312  for (ac=accounts; ac; ac=ac->next) {
313  if (!strcasecmp(ac->name, account_name)) return ac->name;
314  }
315  return NULL;
316 }
317 
332 int account_login(const char *account_name, const char *account_password) {
334 
335  for (ac=accounts; ac; ac=ac->next) {
336  /* Look for a matching account name and check the password. */
337  if (!strcasecmp(ac->name, account_name)) {
338  if (check_password(account_password, ac->password)) {
339  ac->last_login = time(NULL);
340  accounts_logged_in.insert(account_name);
341  return 1;
342  } else {
343  return 0;
344  }
345  }
346  }
347  return 0;
348 }
349 
353 void account_logout(const char *account_name) {
354  accounts_logged_in.erase(account_name);
355 }
356 
376 int account_check_string(const char *str)
377 {
378  const char *cp = str;
379 
380  /* Require first character to be letter or number */
381  if (!isalnum(*str)) return 1;
382  for (; *str != '\0'; ++str) {
383  if (!isprint(*str)) return 1;
384  switch (*str){
385  case ':':
386  case ';':
387  case '/':
388  case '\'':
389  case '[':
390  return 1;
391  }
392  }
393  /* Don't allow space characters at end of string. */
394  if (isspace(*(str-1))) return 1;
395  if ((str - cp) > MAX_NAME) return 2;
396  return 0;
397 }
398 
399 
415 int account_new(const char *account_name, const char *account_password) {
417 
418  // Check password for invalid characters because newhash() may just
419  // return the string in plaintext.
421  return 1;
422 
423  if (account_exists(account_name)) return 2;
424 
425  ac = account_alloc();
426  ac->name = strdup_local(account_name);
427  ac->password = strdup_local(newhash(account_password));
428 
429  /* We put this at the top of the list. This means recent accounts will be at
430  * the top of the file, which is likely a good thing.
431  * We don't do a save right now. When the player associates a character with
432  * the account, we will save them - until that point, not too much reason
433  * to save this out. Note it is still possible for this to get saved out if
434  * another player does something that forces writing out of the accounts file.
435  */
436  ac->next = accounts;
437  accounts = ac;
438 
439  /* mark that accounts should be saved through accounts_save(). */
440  accounts_loaded = 1;
441 
442  return 0;
443 }
444 
461 int account_link(const char *account_name, const char *player_name) {
463 
464  for (ac=accounts; ac; ac=ac->next) {
465  if (!strcasecmp(ac->name, account_name)) break;
466  }
467  if (ac == NULL) return 1;
468 
469  ensure_available_characters(ac, ac->num_characters + 1);
470  ac->character_names[ac->num_characters] = strdup_local(player_name);
471  ac->num_characters++;
472  return 0;
473 }
474 
493 int account_remove_player(const char *account_name, const char *player_name) {
495  int i, match=0;
496 
497  if (account_name == NULL)
498  return 0;
499 
500  for (ac=accounts; ac; ac=ac->next) {
501  if (!strcasecmp(ac->name, account_name)) break;
502  }
503  if (ac == NULL) return 1;
504 
505  /* Try to find the character name. Once we find it, we set match, and
506  * then move the remain character names down by one. The array is
507  * always null terminated, so this also makes sure we copy the null down.
508  */
509  for (i=0; i<ac->num_characters; i++) {
510  if (!strcmp(ac->character_names[i], player_name)) {
511  free(ac->character_names[i]);
512  match=1;
513  }
514  if (match == 1) {
515  ac->character_names[i] = ac->character_names[i+1];
516  }
517  }
518 
519  if (match) {
520  ac->num_characters--;
521  return 0;
522  }
523  /* Otherwise, did not find player name */
524  return 2;
525 }
526 
527 
539 char **account_get_players_for_account(const char *account_name)
540 {
542 
543  for (ac=accounts; ac; ac=ac->next) {
544  if (!strcasecmp(ac->name, account_name)) return ac->character_names;
545  }
546  return NULL;
547 }
548 
555 static int char_in_list(const char *name, const Account_Char *chars) {
556  while (chars) {
557  if (strcmp(chars->name, name) == 0) {
558  return 1;
559  }
560  chars = chars->next;
561  }
562  return 0;
563 }
564 
573 linked_char *account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count) {
575  linked_char *ret = NULL;
576 
577  for (ac = accounts; ac; ac = ac->next) {
578  if (!strcasecmp(ac->name, account_name)) {
579  for (int i = 0; i < ac->num_characters; i++) {
580  if (!char_in_list(ac->character_names[i], chars->chars)) {
581  linked_char *lc = (linked_char *)calloc(1, sizeof(linked_char));
582  lc->next = ret;
583  lc->name = ac->character_names[i];
584  ret = lc;
585  (*count)++;
586  }
587  }
588  return ret;
589  }
590  }
591  return NULL;
592 }
593 
603 const char *account_get_account_for_char(const char *charname)
604 {
606  int i;
607 
608  for (ac=accounts; ac; ac=ac->next) {
609  for (i=0; i<ac->num_characters; i++) {
610  if (!strcmp(ac->character_names[i], charname)) {
611  return ac->name;
612  }
613  }
614  }
615  return NULL;
616 
617 }
618 
629 int account_is_logged_in(const char *name) {
630  return accounts_logged_in.find(name) != accounts_logged_in.end();
631 }
632 
652 int account_change_password(const char *account_name,
653  const char *current_password, const char *new_password) {
655 
656  // Check password for invalid characters as in account_new().
657  if (account_check_string(account_name) ||
658  (current_password != NULL && account_check_string(current_password)) ||
659  account_check_string(new_password)) {
660  return 1;
661  }
662 
663  // Iterate through accounts list until a matching name is found.
664  for (ac = accounts; ac; ac = ac->next) {
665  if (!strcasecmp(ac->name, account_name)) {
666  break;
667  }
668  }
669 
670  // Check if the given account actually exists.
671  if (ac == NULL) {
672  return 2;
673  }
674 
675  // Return an error if the current password does not match.
676  if (current_password != NULL && !check_password(current_password, ac->password)) {
677  return 3;
678  }
679 
680  free(ac->password);
681  ac->password = strdup_local(newhash(new_password));
682 
683  return 0;
684 }
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:539
settings
struct Settings settings
Definition: init.cpp:139
account_logout
void account_logout(const char *account_name)
Definition: account.cpp:353
account_struct
Definition: account.cpp:81
account_alloc
static account_struct * account_alloc()
Definition: account.cpp:137
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:51
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:573
accounts_save
void accounts_save(void)
Definition: account.cpp:266
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:308
account_char_struct::name
sstring name
Definition: account_char.h:13
Ice.tmp
int tmp
Definition: Ice.py:207
accounts_loaded
static int accounts_loaded
Definition: account.cpp:104
account_struct::next
struct account_struct * next
Definition: account.cpp:90
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:493
account_chars_struct
Definition: account_char.h:28
account_char_struct
Definition: account_char.h:12
buf
StringBuffer * buf
Definition: readable.cpp:1611
linked_char
Definition: global.h:89
account_struct
struct account_struct account_struct
accounts_clear
void accounts_clear(void)
Definition: account.cpp:154
account_password
void account_password(char *buf, int len, socket_struct *ns)
Definition: request.cpp:2981
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:90
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:483
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:24
linked_char::next
struct linked_char * next
Definition: global.h:91
MAX_NAME
#define MAX_NAME
Definition: define.h:41
char_in_list
static int char_in_list(const char *name, const Account_Char *chars)
Definition: account.cpp:555
disinfect.count
int count
Definition: disinfect.py:7
push.match
bool match
Definition: push.py:61
account_char_struct::next
struct account_char_struct * next
Definition: account_char.h:21
sproto.h
ACCOUNT_FILE
#define ACCOUNT_FILE
Definition: account.cpp:110
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:580
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:117
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
account_chars_struct::chars
Account_Char * chars
Definition: account_char.h:31
strcasecmp
int strcasecmp(const char *s1, const char *s2)
accounts
static account_struct * accounts
Definition: account.cpp:96
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
Definition: account.cpp:603
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:629
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:652
account_write_entry
static void account_write_entry(FILE *fp, account_struct *ac)
Definition: account.cpp:245
account_link
int account_link(const char *account_name, const char *player_name)
Definition: account.cpp:461
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:332
BufferReader
Definition: bufferreader.cpp:21
accounts_load
void accounts_load(void)
Definition: account.cpp:163
object.h
account_check_string
int account_check_string(const char *str)
Definition: account.cpp:376
account_new
int account_new(const char *account_name, const char *account_password)
Definition: account.cpp:415
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