Crossfire Server, Trunk  R20513
account.c
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 "global.h"
51 
52 #include <ctype.h>
53 #include <errno.h>
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #include "object.h"
58 #include "sproto.h"
59 
60 #include "output_file.h"
61 
63 #define NUM_ACCOUNT_FIELDS 6
64 
76 typedef struct account_struct {
77  char *name;
78  char *password;
79  time_t last_login;
84  time_t created;
85  struct account_struct *next;
87 
91 static account_struct *accounts=NULL;
92 
99 static int accounts_loaded = 0;
100 
105 #define ACCOUNT_FILE "accounts"
106 
112 void accounts_clear(void) {
113  accounts = NULL;
114  accounts_loaded = 0;
115 }
116 
121 void accounts_load(void) {
122  char fname[MAX_BUF], buf[VERY_BIG_BUF];
123  FILE *fp;
124  account_struct *ac, *last=NULL;
125  int fields=0;
126 
127  if (accounts != NULL) {
128  LOG(llevError, "account_load_entries(): Called when accounts has been set.\n");
129  return;
130  }
131  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
132  fp=fopen(fname,"r");
133  if (!fp) {
134  /* This may not in fact be a critical error - on a new server,
135  * the accounts file may not yet exist.
136  */
137  LOG(llevInfo,"Warning: Unable to open %s [%s]\n", fname, strerror(errno));
138  return;
139  }
140  while (fgets(buf, VERY_BIG_BUF, fp)) {
141  char *tmp[NUM_ACCOUNT_FIELDS], *cp;
142  int result, i;
143 
144  /* Ignore any comment lines */
145  if (buf[0] == '#') continue;
146 
147  /* remove newline */
148  cp = strchr(buf, '\n');
149  if (cp) *cp='\0';
150 
151  fields = split_string(buf, tmp, NUM_ACCOUNT_FIELDS, ':');
152 
153  ac = malloc(sizeof(account_struct));
154  ac->name = strdup_local(tmp[0]);
155  ac->password = strdup_local(tmp[1]);
156  ac->last_login = strtoul(tmp[2], (char**)NULL, 10);
157 
158  /* While probably no one was using this code before this
159  * field was added, this provides a nice example of handling
160  * additional fields.
161  */
162  if (fields>4) ac->created = strtoul(tmp[4], (char**)NULL, 10);
163  else
164  ac->created = ac->last_login;
165 
166  ac->next = NULL;
167 
168  /* If this is a blank field, nothing to do */
169  if (tmp[3][0] == 0) {
170  ac->num_characters = 0;
171  for (i=0; i <= MAX_CHARACTERS_PER_ACCOUNT; i++)
172  ac->character_names[i] = NULL;
173  } else {
174  /* count up how many semicolons - this is the character
175  * seperator. We start at one, because these are seperators,
176  * so there will be one more name than seperators.
177  */
178  ac->num_characters=1;
179  for (cp = tmp[3]; *cp != '\0'; cp++) {
180  if (*cp == ';') ac->num_characters++;
181  }
182 
183  result = split_string(tmp[3], ac->character_names, ac->num_characters, ';');
184  /* This should never happen, but check for it. Even if we do get it, not necessarily
185  * a critical error - this is why we use calloc above.
186  */
187  if (result != ac->num_characters) {
188  LOG(llevError, "account_load_entries: split_string found different number of characters: %d != %d\n",
189  result, ac->num_characters);
190  }
191 
193  LOG(llevError,"account_load_entries: Too many characters set for account %s - truncating to %d\n",
196  }
197 
198  /* The string data that the names are stored in is currently temporary data
199  * that will go away, so we need to allocate some permanent data
200  * now. Also, if we have a NULL value, means we got the error above -
201  * NULL values would only be at the end of the split, so just reduce
202  * the character count.
203  */
204  for (i=0; i<ac->num_characters; i++) {
205  if (ac->character_names[i] != NULL) {
207  } else {
208  ac->num_characters = i;
209  break;
210  }
211  }
212  /* NULL terminate - in that way, we can just return ac->character_names to
213  * callers that want to know all characters associated with an account,
214  * and it can just iterate until it gets the NULL terminator.
215  * For safety, just set all remaining values to NULL
216  */
217  while (i <= MAX_CHARACTERS_PER_ACCOUNT) {
218  ac->character_names[i] = NULL;
219  i++;
220  }
221  }
222 
223  /* We tack on to the end of the list - in this way,
224  * the order of the file remains the same.
225  */
226  if (last)
227  last->next = ac;
228  else
229  accounts = ac;
230  last = ac;
231 
232  }
233  fclose(fp);
234  accounts_loaded = 1;
235 }
236 
246 static void account_write_entry(FILE *fp, account_struct *ac)
247 {
248  int i;
249 
250  fprintf(fp,"%s:%s:%u:", ac->name, ac->password, (uint32_t)ac->last_login);
251  for (i=0; i<ac->num_characters; i++) {
252  if (i != 0)
253  fprintf(fp,";%s", ac->character_names[i]);
254  else
255  fprintf(fp,"%s", ac->character_names[i]);
256  }
257  fprintf(fp,":%u:\n", (uint32_t) ac->created);
258 }
259 
260 
267 void accounts_save(void)
268 {
269  char fname[MAX_BUF];
270  FILE *fp;
271  OutputFile of;
272  account_struct *ac;
273 
274  if (accounts_loaded == 0)
275  return;
276 
277  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
278 
279  fp = of_open(&of, fname);
280  if (fp == NULL)
281  return;
282 
283  fprintf(fp, "# This file should not be edited while the server is running.\n");
284  fprintf(fp, "# Otherwise, any changes made may be overwritten by the server\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 {
310  account_struct *ac;
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) {
333  account_struct *ac;
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  return 1;
341  } else {
342  return 0;
343  }
344  }
345  }
346  return 0;
347 }
348 
368 int account_check_string(const char *str)
369 {
370  const char *cp = str;
371 
372  /* Require first character to be letter or number */
373  if (!isalnum(*str)) return 1;
374  for (; *str != '\0'; ++str) {
375  if (!isprint(*str)) return 1;
376  switch (*str){
377  case ':':
378  case ';':
379  case '/':
380  case '\'':
381  case '[':
382  return 1;
383  }
384  }
385  /* Don't allow space characters at end of string. */
386  if (isspace(*(str-1))) return 1;
387  if ((str - cp) > MAX_NAME) return 2;
388  return 0;
389 }
390 
391 
407 int account_new(const char *account_name, const char *account_password) {
408  account_struct *ac;
409 
410  // Check password for invalid characters because newhash() may just
411  // return the string in plaintext.
412  if (account_check_string(account_name) || account_check_string(account_password))
413  return 1;
414 
415  if (account_exists(account_name)) return 2;
416 
417  ac = malloc(sizeof(account_struct));
418  ac->name = strdup_local(account_name);
419  ac->password = strdup_local(newhash(account_password));
420  ac->last_login = time(NULL);
421  ac->created = ac->last_login;
422  ac->num_characters = 0;
423 
424  memset(ac->character_names, 0, MAX_CHARACTERS_PER_ACCOUNT+1 * sizeof(char*));
425 
426  /* We put this at the top of the list. This means recent accounts will be at
427  * the top of the file, which is likely a good thing.
428  * We don't do a save right now. When the player associates a character with
429  * the account, we will save them - until that point, not too much reason
430  * to save this out. Note it is still possible for this to get saved out if
431  * another player does something that forces writing out of the accounts file.
432  */
433  ac->next = accounts;
434  accounts = ac;
435 
436  /* mark that accounts should be saved through accounts_save(). */
437  accounts_loaded = 1;
438 
439  return 0;
440 }
441 
460 int account_link(const char *account_name, const char *player_name) {
461  account_struct *ac;
462 
463  for (ac=accounts; ac; ac=ac->next) {
464  if (!strcasecmp(ac->name, account_name)) break;
465  }
466  if (ac == NULL) return 1;
467 
468  if (ac->num_characters >= MAX_CHARACTERS_PER_ACCOUNT) return 2;
469 
470  ac->character_names[ac->num_characters] = strdup_local(player_name);
471  ac->num_characters++;
472  /* NULL terminate, as per notes above. In theory not necessary since data
473  * is all set to NULL */
474  ac->character_names[ac->num_characters] = NULL;
475  return 0;
476 }
477 
496 int account_remove_player(const char *account_name, const char *player_name) {
497  account_struct *ac;
498  int i, match=0;
499 
500  if (account_name == NULL)
501  return 0;
502 
503  for (ac=accounts; ac; ac=ac->next) {
504  if (!strcasecmp(ac->name, account_name)) break;
505  }
506  if (ac == NULL) return 1;
507 
508  /* Try to find the character name. Once we find it, we set match, and
509  * then move the remain character names down by one. The array is
510  * always null terminated, so this also makes sure we copy the null down.
511  */
512  for (i=0; i<ac->num_characters; i++) {
513  if (!strcmp(ac->character_names[i], player_name)) {
514  free(ac->character_names[i]);
515  match=1;
516  }
517  if (match == 1) {
518  ac->character_names[i] = ac->character_names[i+1];
519  }
520  }
521 
522  if (match) {
523  ac->num_characters--;
524  return 0;
525  }
526  /* Otherwise, did not find player name */
527  return 2;
528 }
529 
530 
542 char **account_get_players_for_account(const char *account_name)
543 {
544  account_struct *ac;
545 
546  for (ac=accounts; ac; ac=ac->next) {
547  if (!strcasecmp(ac->name, account_name)) return ac->character_names;
548  }
549  return NULL;
550 }
551 
561 const char *account_get_account_for_char(const char *charname)
562 {
563  account_struct *ac;
564  int i;
565 
566  for (ac=accounts; ac; ac=ac->next) {
567  for (i=0; i<ac->num_characters; i++) {
568  if (!strcmp(ac->character_names[i], charname)) {
569  return ac->name;
570  }
571  }
572  }
573  return NULL;
574 
575 }
576 
587 {
588  player *pl;
589 
590  for (pl = first_player; pl; pl=pl->next) {
591  if (pl->socket.account_name &&
592  !strcasecmp(pl->socket.account_name, name)) return pl;
593  }
594  return NULL;
595 }
596 
608 {
609  int i;
610 
611  for (i=0; i < socket_info.allocated_sockets; i++) {
612  if (init_sockets[i].status == Ns_Add &&
613  init_sockets[i].account_name &&
614  !strcasecmp(init_sockets[i].account_name, name)) return(&init_sockets[i]);
615  }
616  return NULL;
617 }
618 
629 int account_is_logged_in(const char *name)
630 {
631  if (account_get_logged_in_player(name)) return 1;
632 
633  if (account_get_logged_in_init_socket(name)!=NULL) return 1;
634 
635  return 0;
636 }
637 
657 int account_change_password(const char *account_name,
658  const char *current_password, const char *new_password) {
659  account_struct *ac;
660 
661  // Check password for invalid characters as in account_new().
662  if (account_check_string(account_name) || account_check_string(current_password) ||
663  account_check_string(new_password)) {
664  return 1;
665  }
666 
667  // Iterate through accounts list until a matching name is found.
668  for (ac = accounts; ac; ac = ac->next) {
669  if (!strcasecmp(ac->name, account_name)) {
670  break;
671  }
672  }
673 
674  // Check if the given account actually exists.
675  if (ac == NULL) {
676  return 2;
677  }
678 
679  // Return an error if the current password does not match.
680  if (!check_password(current_password, ac->password)) {
681  return 3;
682  }
683 
684  free(ac->password);
685  ac->password = strdup_local(newhash(new_password));
686 
687  return 0;
688 }
Error, serious thing.
Definition: logger.h:11
One player.
Definition: player.h:92
Information.
Definition: logger.h:12
char * account_name
Name of the account logged in on this socket.
Definition: newserver.h:138
bool check_password(const char *typed, const char *crypted)
Hash a password and compare it to the stored version.
Definition: server.c:100
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.c:112
#define strdup_local
Definition: compat.h:25
int num_characters
Number of characters on this account.
Definition: account.c:80
char * password
Password for this account.
Definition: account.c:78
char * character_names[MAX_CHARACTERS_PER_ACCOUNT+1]
Character names associated with this account +1 added to allow for NULL termination.
Definition: account.c:81
Socket structure, represents a client-server connection.
Definition: newserver.h:99
#define NUM_ACCOUNT_FIELDS
Number of fields in the accounts file.
Definition: account.c:63
struct account_struct * next
Next in list.
Definition: account.c:85
socket_struct socket
Socket information for this player.
Definition: player.h:94
void accounts_load(void)
This loads all the account entries into memory.
Definition: account.c:121
int account_link(const char *account_name, const char *player_name)
Adds a player name to an account.
Definition: account.c:460
player * account_get_logged_in_player(const char *name)
This checks to see if the account is logged in with a player attached If so, it returns the player ob...
Definition: account.c:586
Global type definitions and header inclusions.
socket_struct * init_sockets
Established connections for clients not yet playing.
Definition: init.c:56
time_t created
When character was created.
Definition: account.c:84
const char * account_exists(const char *account_name)
Checks the existing accounts, and see if this account exists.
Definition: account.c:308
time_t last_login
Last time this account was logged in.
Definition: account.c:79
Socket_Info socket_info
Socket information.
Definition: init.c:47
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.c:332
#define ACCOUNT_FILE
Name of the accounts file.
Definition: account.c:105
#define snprintf
Definition: win32.h:46
char const * newhash(char const password[static 1])
Definition: server.c:87
void account_password(char *buf, int len, socket_struct *ns)
Handles the account password change.
Definition: request.c:2862
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.c:61
static int accounts_loaded
Whether the account information was loaded or not.
Definition: account.c:99
int account_remove_player(const char *account_name, const char *player_name)
Removes a player name from an account.
Definition: account.c:496
int allocated_sockets
Number of allocated items in init_sockets.
Definition: newserver.h:156
struct account_struct account_struct
Structure that holds account data.
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
socket_struct * account_get_logged_in_init_socket(const char *name)
This is like the above routine, but checks the init_sockets (account in process of logging in)...
Definition: account.c:607
unsigned int uint32_t
Definition: win32.h:162
int account_new(const char *account_name, const char *account_password)
Adds an account.
Definition: account.c:407
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.c:542
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
Change an account password.
Definition: account.c:657
Object structure, the core of Crossfire.
#define VERY_BIG_BUF
Definition: define.h:36
const char * localdir
Read/write data files.
Definition: global.h:245
Structure that holds account data.
Definition: account.c:76
struct Settings settings
Server settings.
Definition: init.c:40
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.c:30
Functions for creating text output files.
#define MAX_CHARACTERS_PER_ACCOUNT
The maximum characters per account is really driven by the size of the buffer we use to read in the d...
Definition: account_char.h:20
void accounts_save(void)
Save all the account information.
Definition: account.c:267
EXTERN player * first_player
First player.
Definition: global.h:117
struct pl * next
Pointer to next player, NULL if this is last.
Definition: player.h:93
int strcasecmp(const char *s1, const char *s2)
Case-insensitive comparaison of strings.
Definition: porting.c:256
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.c:499
char * name
Account name.
Definition: account.c:77
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
int account_is_logged_in(const char *name)
This checkes if an account is logged in.
Definition: account.c:629
#define MAX_NAME
Definition: define.h:41
int account_check_string(const char *str)
Checks a string to make sure it does not have any invalid characters.
Definition: account.c:368
static void account_write_entry(FILE *fp, account_struct *ac)
This writes a single account entry to the given filepointer.
Definition: account.c:246
static account_struct * accounts
list of all accounts.
Definition: account.c:91
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.c:561