Crossfire Server, Trunk  1.75.0
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 
52 #include "global.h"
53 
55 static std::set<std::string> accounts_logged_in = std::set<std::string>();
56 
57 #include <ctype.h>
58 #include <stdlib.h>
59 #include <string.h>
60 
61 #include "object.h"
62 #include "sproto.h"
63 
64 #include "output_file.h"
65 
67 #define NUM_ACCOUNT_FIELDS 6
68 
81  char *name;
82  char *password;
83  time_t last_login;
86  char **character_names;
88  time_t created;
89 };
90 
94 static std::vector<account_struct *> accounts;
95 
102 static int accounts_loaded = 0;
103 
108 #define ACCOUNT_FILE "accounts"
109 
115 static void ensure_available_characters(account_struct *account, int count) {
116  if (count >= account->allocated_characters) { // The list is NULL-terminated
117  const int pa = account->allocated_characters;
118  account->allocated_characters = count + 1;
119  account->character_names = static_cast<char **>(realloc(account->character_names, account->allocated_characters * sizeof(account->character_names[0])));
120  if (!account->character_names) {
121  LOG(llevError, "Unable to allocate %d characters names!", account->allocated_characters);
123  }
124  for (int i = pa; i < account->allocated_characters; i++) {
125  account->character_names[i] = NULL;
126  }
127  }
128 }
129 
136  account_struct *ac = (account_struct *)calloc(1, sizeof(account_struct));
137  if (!ac) {
138  LOG(llevError, "Unable to allocate an account_struct!\n");
140  }
141  ac->last_login = time(NULL);
142  ac->created = ac->last_login;
144  return ac;
145 }
146 
152 void accounts_clear(void) {
153  accounts.clear();
154  accounts_loaded = 0;
155 }
156 
161 void accounts_load(void) {
162  char fname[MAX_BUF], *buf;
163  int fields=0;
164  BufferReader *br;
165 
166  if (!accounts.empty()) {
167  LOG(llevError, "account_load_entries(): Called when accounts has been set.\n");
168  return;
169  }
170  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
171  if ((br = bufferreader_init_from_file(NULL, fname, "Could not open %s (%s)", llevDebug)) == NULL) {
172  return;
173  }
174 
175  while ((buf = bufferreader_next_line(br))) {
176  char *tmp[NUM_ACCOUNT_FIELDS], *cp;
177  int i;
178 
179  /* Ignore any comment lines */
180  if (buf[0] == '#') continue;
181 
182  fields = split_string(buf, tmp, NUM_ACCOUNT_FIELDS, ':');
183 
185  ac->name = strdup_local(tmp[0]);
186  ac->password = strdup_local(tmp[1]);
187  ac->last_login = strtoul(tmp[2], (char**)NULL, 10);
188 
189  /* While probably no one was using this code before this
190  * field was added, this provides a nice example of handling
191  * additional fields.
192  */
193  if (fields>4) ac->created = strtoul(tmp[4], (char**)NULL, 10);
194  else
195  ac->created = ac->last_login;
196 
197  /* If this is a blank field, nothing to do */
198  if (tmp[3][0]) {
199  /* count up how many semicolons - this is the character
200  * separator. We start at one, because these are separators,
201  * so there will be one more name than separators.
202  */
203  ac->num_characters=1;
204  for (cp = tmp[3]; *cp != '\0'; cp++) {
205  if (*cp == ';') ac->num_characters++;
206  }
208 
209  split_string(tmp[3], ac->character_names, ac->num_characters, ';');
210 
211  /* The string data that the names are stored in is currently temporary data
212  * that will go away, so we need to allocate some permanent data now */
213  for (i=0; i<ac->num_characters; i++) {
215  }
216  }
217 
218  accounts.push_back(ac);
219  }
220 
222  accounts_loaded = 1;
223 }
224 
234 static void account_write_entry(FILE *fp, account_struct *ac)
235 {
236  int i;
237 
238  fprintf(fp,"%s:%s:%u:", ac->name, ac->password, (uint32_t)ac->last_login);
239  for (i=0; i<ac->num_characters; i++) {
240  if (i != 0)
241  fprintf(fp,";%s", ac->character_names[i]);
242  else
243  fprintf(fp,"%s", ac->character_names[i]);
244  }
245  fprintf(fp,":%u:\n", (uint32_t) ac->created);
246 }
247 
248 
255 void accounts_save(void)
256 {
257  char fname[MAX_BUF];
258  FILE *fp;
259  OutputFile of;
260 
261  if (accounts_loaded == 0)
262  return;
263 
264  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
265 
266  fp = of_open(&of, fname);
267  if (fp == NULL)
268  return;
269 
270  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
271  "# only read when the server starts, and any changes will be overwritten when\n"
272  "# the server exits.\n");
273  fprintf(fp, "# Format:\n");
274  fprintf(fp, "# Account name:Password:Account last used:Characters (semicolon separated):created:expansion\n");
275  for (auto ac : accounts) {
276  /* Don't write out accounts with no characters associated unless the
277  * account is at least a day old.
278  */
279  // 86400 seconds in a day, so no reason to make the game have to recalculate this all the time
280  // SilverNexus 2014-06-12
281  if (ac->num_characters || (ac->created > (time(NULL) - 86400)))
282  account_write_entry(fp, ac);
283  }
284  of_close(&of);
285 }
286 
296 const char *account_exists(const char *account_name)
297 {
298  for (auto ac : accounts) {
299  if (!strcasecmp(ac->name, account_name)) return ac->name;
300  }
301  return NULL;
302 }
303 
318 int account_login(const char *account_name, const char *account_password) {
319  for (auto ac : accounts) {
320  /* Look for a matching account name and check the password. */
321  if (!strcasecmp(ac->name, account_name)) {
322  if (check_password(account_password, ac->password)) {
323  ac->last_login = time(NULL);
324  accounts_logged_in.insert(account_name);
325  return 1;
326  } else {
327  return 0;
328  }
329  }
330  }
331  return 0;
332 }
333 
337 void account_logout(const char *account_name) {
338  accounts_logged_in.erase(account_name);
339 }
340 
360 int account_check_string(const char *str)
361 {
362  const char *cp = str;
363 
364  /* Require first character to be letter or number */
365  if (!isalnum(*str)) return 1;
366  for (; *str != '\0'; ++str) {
367  if (!isprint(*str)) return 1;
368  switch (*str){
369  case ':':
370  case ';':
371  case '/':
372  case '\'':
373  case '[':
374  return 1;
375  }
376  }
377  /* Don't allow space characters at end of string. */
378  if (isspace(*(str-1))) return 1;
379  if ((str - cp) > MAX_NAME) return 2;
380  return 0;
381 }
382 
383 
399 int account_new(const char *account_name, const char *account_password) {
400  account_struct *ac;
401 
402  // Check password for invalid characters because newhash() may just
403  // return the string in plaintext.
405  return 1;
406 
407  if (account_exists(account_name)) return 2;
408 
409  ac = account_alloc();
410  ac->name = strdup_local(account_name);
412 
413  /* We put this at the top of the list. This means recent accounts will be at
414  * the top of the file, which is likely a good thing.
415  * We don't do a save right now. When the player associates a character with
416  * the account, we will save them - until that point, not too much reason
417  * to save this out. Note it is still possible for this to get saved out if
418  * another player does something that forces writing out of the accounts file.
419  */
420  accounts.insert(accounts.begin(), ac);
421 
422  /* mark that accounts should be saved through accounts_save(). */
423  accounts_loaded = 1;
424 
425  return 0;
426 }
427 
444 int account_link(const char *account_name, const char *player_name) {
445  for (auto ac : accounts) {
446  if (!strcasecmp(ac->name, account_name)) {
447  ensure_available_characters(ac, ac->num_characters + 1);
448  ac->character_names[ac->num_characters] = strdup_local(player_name);
449  ac->num_characters++;
450  return 0;
451  }
452  }
453  return 1;
454 }
455 
474 int account_remove_player(const char *account_name, const char *player_name) {
475  int i, match=0;
476 
477  if (account_name == NULL)
478  return 0;
479 
480  for (auto ac : accounts) {
481  if (!strcasecmp(ac->name, account_name)) {
482  /* Try to find the character name. Once we find it, we set match, and
483  * then move the remain character names down by one. The array is
484  * always null terminated, so this also makes sure we copy the null down.
485  */
486  for (i=0; i<ac->num_characters; i++) {
487  if (!strcmp(ac->character_names[i], player_name)) {
488  free(ac->character_names[i]);
489  match=1;
490  }
491  if (match == 1) {
492  ac->character_names[i] = ac->character_names[i+1];
493  }
494  }
495 
496  if (match) {
497  ac->num_characters--;
498  return 0;
499  }
500  /* Otherwise, did not find player name */
501  return 2;
502  }
503  }
504  return 1;
505 }
506 
507 
519 char **account_get_players_for_account(const char *account_name)
520 {
521  for (auto ac : accounts) {
522  if (!strcasecmp(ac->name, account_name)) return ac->character_names;
523  }
524  return NULL;
525 }
526 
533 static int char_in_list(const char *name, const std::vector<Account_Char *> chars) {
534  for (auto ch : chars) {
535  if (strcmp(ch->name, name) == 0) {
536  return 1;
537  }
538  }
539  return 0;
540 }
541 
550 linked_char *account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count) {
551  linked_char *ret = NULL;
552 
553  for (auto ac : accounts) {
554  if (!strcasecmp(ac->name, account_name)) {
555  for (int i = 0; i < ac->num_characters; i++) {
556  if (!char_in_list(ac->character_names[i], chars->chars)) {
557  linked_char *lc = (linked_char *)calloc(1, sizeof(linked_char));
558  lc->next = ret;
559  lc->name = ac->character_names[i];
560  ret = lc;
561  (*count)++;
562  }
563  }
564  return ret;
565  }
566  }
567  return NULL;
568 }
569 
579 const char *account_get_account_for_char(const char *charname)
580 {
581  int i;
582 
583  for (auto ac : accounts) {
584  for (i=0; i<ac->num_characters; i++) {
585  if (!strcmp(ac->character_names[i], charname)) {
586  return ac->name;
587  }
588  }
589  }
590  return NULL;
591 
592 }
593 
604 int account_is_logged_in(const char *name) {
605  return accounts_logged_in.find(name) != accounts_logged_in.end();
606 }
607 
627 int account_change_password(const char *account_name,
628  const char *current_password, const char *new_password) {
629 
630  // Check password for invalid characters as in account_new().
631  if (account_check_string(account_name) ||
632  (current_password != NULL && account_check_string(current_password)) ||
633  account_check_string(new_password)) {
634  return 1;
635  }
636 
637  // Iterate through accounts list until a matching name is found.
638  for (auto ac : accounts) {
639  if (!strcasecmp(ac->name, account_name)) {
640  // Return an error if the current password does not match.
641  if (current_password != NULL && !check_password(current_password, ac->password)) {
642  return 3;
643  }
644 
645  free(ac->password);
646  ac->password = strdup_local(newhash(new_password));
647 
648  return 0;
649  }
650  }
651 
652  return 2;
653 }
output_file.h
global.h
account_struct::name
char * name
Account name.
Definition: account.cpp:81
account_get_players_for_account
char ** account_get_players_for_account(const char *account_name)
Returns an array of strings for the characters on this account - the array is null terminated.
Definition: account.cpp:519
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
account_logout
void account_logout(const char *account_name)
Remove 'account_name' from the list of logged in accounts.
Definition: account.cpp:337
account_struct
Structure that holds account data.
Definition: account.cpp:80
account_alloc
static account_struct * account_alloc()
Allocate a new account_struct item.
Definition: account.cpp:135
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
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
time
same as sound ncom command like but with extra the client want tick commands so it knows animation timing the client wants to be informed of pickup mode changes Mode will be sent when the player successfully logs and afterward any time the value is but over time
Definition: protocol.txt:416
account_get_additional_chars
linked_char * account_get_additional_chars(const char *account_name, const Account_Chars *chars, int *count)
Get a list of character names linked to the specified account which are not listed in chars.
Definition: account.cpp:550
accounts_save
void accounts_save(void)
Save all the account information.
Definition: account.cpp:255
account_struct::created
time_t created
When character was created.
Definition: account.cpp:88
bufferreader_destroy
void bufferreader_destroy(BufferReader *br)
Destroy a BufferReader.
Definition: bufferreader.cpp:41
account_exists
const char * account_exists(const char *account_name)
Checks the existing accounts, and see if this account exists.
Definition: account.cpp:296
accounts_loaded
static int accounts_loaded
Whether the account information was loaded or not.
Definition: account.cpp:102
account_struct::num_characters
int num_characters
Number of characters on this account.
Definition: account.cpp:84
account_remove_player
int account_remove_player(const char *account_name, const char *player_name)
Removes a player name from an account.
Definition: account.cpp:474
accounts
static std::vector< account_struct * > accounts
list of all accounts.
Definition: account.cpp:94
char_in_list
static int char_in_list(const char *name, const std::vector< Account_Char * > chars)
Check if a character name is in a list or not.
Definition: account.cpp:533
buf
StringBuffer * buf
Definition: readable.cpp:1564
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
linked_char
Definition: global.h:102
Account_Chars::chars
std::vector< Account_Char * > chars
Characters of the account.
Definition: account_char.h:30
accounts_clear
void accounts_clear(void)
This is used purely by the test harness - by clearing the accounts, it can then verify that the data ...
Definition: account.cpp:152
account_password
void account_password(char *buf, int len, socket_struct *ns)
Handles the account password change.
Definition: request.cpp:3103
bufferreader_init_from_file
BufferReader * bufferreader_init_from_file(BufferReader *br, const char *filepath, const char *failureMessage, LogLevel failureLevel)
Initialize or create a BufferReader from a file path.
Definition: bufferreader.cpp:67
linked_char::name
const char * name
Definition: global.h:103
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings.
Definition: utils.cpp:478
account_struct::last_login
time_t last_login
Last time this account was logged in.
Definition: account.cpp:83
NUM_ACCOUNT_FIELDS
#define NUM_ACCOUNT_FIELDS
Number of fields in the accounts file.
Definition: account.cpp:67
linked_char::next
struct linked_char * next
Definition: global.h:104
MAX_NAME
#define MAX_NAME
Definition: define.h:41
sproto.h
ACCOUNT_FILE
#define ACCOUNT_FILE
Name of the accounts file.
Definition: account.cpp:108
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:595
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
ensure_available_characters
static void ensure_available_characters(account_struct *account, int count)
Ensure an account can handle at least the specified count of character names.
Definition: account.cpp:115
account_struct::allocated_characters
int allocated_characters
Number of allocated items in character_names.
Definition: account.cpp:85
accounts_logged_in
static std::set< std::string > accounts_logged_in
Set of accounts names that are currently logged in.
Definition: account.cpp:55
strcasecmp
int strcasecmp(const char *s1, const char *s2)
Account_Chars
Structure handling character information for an account.
Definition: account_char.h:27
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
This looks at all the accounts and sees if charname is associated with any of them.
Definition: account.cpp:579
check_password
bool check_password(const char *typed, const char *crypted)
Hash a password and compare it to the stored version.
Definition: server.cpp:114
account_is_logged_in
int account_is_logged_in(const char *name)
This checkes if an account is logged in.
Definition: account.cpp:604
account_struct::character_names
char ** character_names
Character names associated with this account, +1 added to allow for NULL termination.
Definition: account.cpp:86
account_change_password
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
Change an account password.
Definition: account.cpp:627
account_write_entry
static void account_write_entry(FILE *fp, account_struct *ac)
This writes a single account entry to the given filepointer.
Definition: account.cpp:234
account_link
int account_link(const char *account_name, const char *player_name)
Adds a player name to an account.
Definition: account.cpp:444
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
account_login
int account_login(const char *account_name, const char *account_password)
Check if the given account exists, and whether the password is correct.
Definition: account.cpp:318
BufferReader
Definition: bufferreader.cpp:22
accounts_load
void accounts_load(void)
This loads all the account entries into memory.
Definition: account.cpp:161
object.h
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:15
account_check_string
int account_check_string(const char *str)
Checks a string to make sure it does not have any invalid characters.
Definition: account.cpp:360
account_new
int account_new(const char *account_name, const char *account_password)
Adds an account.
Definition: account.cpp:399
account_struct::password
char * password
Password for this account.
Definition: account.cpp:82
OutputFile
Definition: output_file.h:41
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
Definition: bufferreader.cpp:104
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254