Crossfire Server, Trunk
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 <stdlib.h>
54 #include <string.h>
55 
56 #include "object.h"
57 #include "sproto.h"
58 
59 #include "output_file.h"
60 
62 #define NUM_ACCOUNT_FIELDS 6
63 
75 typedef struct account_struct {
76  char *name;
77  char *password;
78  time_t last_login;
81  char **character_names;
83  time_t created;
84  struct account_struct *next;
86 
90 static account_struct *accounts=NULL;
91 
98 static int accounts_loaded = 0;
99 
104 #define ACCOUNT_FILE "accounts"
105 
112  if (count >= account->allocated_characters) { // The list is NULL-terminated
113  const int pa = account->allocated_characters;
114  account->allocated_characters = count + 1;
115  account->character_names = realloc(account->character_names, account->allocated_characters * sizeof(account->character_names[0]));
116  if (!account->character_names) {
117  LOG(llevError, "Unable to allocate %d characters names!", account->allocated_characters);
119  }
120  for (int i = pa; i < account->allocated_characters; i++) {
121  account->character_names[i] = NULL;
122  }
123  }
124 }
125 
132  account_struct *ac = (account_struct *)calloc(1, sizeof(account_struct));
133  if (!ac) {
134  LOG(llevError, "Unable to allocate an account_struct!\n");
136  }
137  ac->last_login = time(NULL);
138  ac->created = ac->last_login;
140  return ac;
141 }
142 
148 void accounts_clear(void) {
149  accounts = NULL;
150  accounts_loaded = 0;
151 }
152 
157 void accounts_load(void) {
158  char fname[MAX_BUF], *buf;
159  account_struct *ac, *last=NULL;
160  int fields=0;
161  BufferReader *br;
162 
163  if (accounts != NULL) {
164  LOG(llevError, "account_load_entries(): Called when accounts has been set.\n");
165  return;
166  }
167  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
168  if ((br = bufferreader_init_from_file(NULL, fname, "Warning: Unable to open %s [%s]\n", llevInfo)) == NULL) {
169  return;
170  }
171 
172  while ((buf = bufferreader_next_line(br))) {
173  char *tmp[NUM_ACCOUNT_FIELDS], *cp;
174  int i;
175 
176  /* Ignore any comment lines */
177  if (buf[0] == '#') continue;
178 
179  fields = split_string(buf, tmp, NUM_ACCOUNT_FIELDS, ':');
180 
181  ac = account_alloc();
182  ac->name = strdup_local(tmp[0]);
183  ac->password = strdup_local(tmp[1]);
184  ac->last_login = strtoul(tmp[2], (char**)NULL, 10);
185 
186  /* While probably no one was using this code before this
187  * field was added, this provides a nice example of handling
188  * additional fields.
189  */
190  if (fields>4) ac->created = strtoul(tmp[4], (char**)NULL, 10);
191  else
192  ac->created = ac->last_login;
193 
194  /* If this is a blank field, nothing to do */
195  if (tmp[3][0]) {
196  /* count up how many semicolons - this is the character
197  * seperator. We start at one, because these are seperators,
198  * so there will be one more name than seperators.
199  */
200  ac->num_characters=1;
201  for (cp = tmp[3]; *cp != '\0'; cp++) {
202  if (*cp == ';') ac->num_characters++;
203  }
204  ensure_available_characters(ac, ac->num_characters);
205 
206  split_string(tmp[3], ac->character_names, ac->num_characters, ';');
207 
208  /* The string data that the names are stored in is currently temporary data
209  * that will go away, so we need to allocate some permanent data now */
210  for (i=0; i<ac->num_characters; i++) {
211  ac->character_names[i] = strdup_local(ac->character_names[i]);
212  }
213  }
214 
215  /* We tack on to the end of the list - in this way,
216  * the order of the file remains the same.
217  */
218  if (last)
219  last->next = ac;
220  else
221  accounts = ac;
222  last = ac;
223 
224  }
225 
227  accounts_loaded = 1;
228 }
229 
239 static void account_write_entry(FILE *fp, account_struct *ac)
240 {
241  int i;
242 
243  fprintf(fp,"%s:%s:%u:", ac->name, ac->password, (uint32_t)ac->last_login);
244  for (i=0; i<ac->num_characters; i++) {
245  if (i != 0)
246  fprintf(fp,";%s", ac->character_names[i]);
247  else
248  fprintf(fp,"%s", ac->character_names[i]);
249  }
250  fprintf(fp,":%u:\n", (uint32_t) ac->created);
251 }
252 
253 
260 void accounts_save(void)
261 {
262  char fname[MAX_BUF];
263  FILE *fp;
264  OutputFile of;
266 
267  if (accounts_loaded == 0)
268  return;
269 
270  snprintf(fname, MAX_BUF,"%s/%s", settings.localdir, ACCOUNT_FILE);
271 
272  fp = of_open(&of, fname);
273  if (fp == NULL)
274  return;
275 
276  fprintf(fp, "# IMPORTANT: Do not edit this file while the server is running. This file is\n"
277  "# only read when the server starts, and any changes will be overwritten when\n"
278  "# the server exits.\n");
279  fprintf(fp, "# Format:\n");
280  fprintf(fp, "# Account name:Password:Account last used:Characters (semicolon separated):created:expansion\n");
281  for (ac=accounts; ac; ac=ac->next) {
282  /* Don't write out accounts with no characters associated unless the
283  * account is at least a day old.
284  */
285  // 86400 seconds in a day, so no reason to make the game have to recalculate this all the time
286  // SilverNexus 2014-06-12
287  if (ac->num_characters || (ac->created > (time(NULL) - 86400)))
288  account_write_entry(fp, ac);
289  }
290  of_close(&of);
291 }
292 
302 const char *account_exists(const char *account_name)
303 {
305 
306  for (ac=accounts; ac; ac=ac->next) {
307  if (!strcasecmp(ac->name, account_name)) return ac->name;
308  }
309  return NULL;
310 }
311 
326 int account_login(const char *account_name, const char *account_password) {
328 
329  for (ac=accounts; ac; ac=ac->next) {
330  /* Look for a matching account name and check the password. */
331  if (!strcasecmp(ac->name, account_name)) {
332  if (check_password(account_password, ac->password)) {
333  ac->last_login = time(NULL);
334  return 1;
335  } else {
336  return 0;
337  }
338  }
339  }
340  return 0;
341 }
342 
362 int account_check_string(const char *str)
363 {
364  const char *cp = str;
365 
366  /* Require first character to be letter or number */
367  if (!isalnum(*str)) return 1;
368  for (; *str != '\0'; ++str) {
369  if (!isprint(*str)) return 1;
370  switch (*str){
371  case ':':
372  case ';':
373  case '/':
374  case '\'':
375  case '[':
376  return 1;
377  }
378  }
379  /* Don't allow space characters at end of string. */
380  if (isspace(*(str-1))) return 1;
381  if ((str - cp) > MAX_NAME) return 2;
382  return 0;
383 }
384 
385 
401 int account_new(const char *account_name, const char *account_password) {
403 
404  // Check password for invalid characters because newhash() may just
405  // return the string in plaintext.
407  return 1;
408 
409  if (account_exists(account_name)) return 2;
410 
411  ac = account_alloc();
412  ac->name = strdup_local(account_name);
413  ac->password = strdup_local(newhash(account_password));
414 
415  /* We put this at the top of the list. This means recent accounts will be at
416  * the top of the file, which is likely a good thing.
417  * We don't do a save right now. When the player associates a character with
418  * the account, we will save them - until that point, not too much reason
419  * to save this out. Note it is still possible for this to get saved out if
420  * another player does something that forces writing out of the accounts file.
421  */
422  ac->next = accounts;
423  accounts = ac;
424 
425  /* mark that accounts should be saved through accounts_save(). */
426  accounts_loaded = 1;
427 
428  return 0;
429 }
430 
447 int account_link(const char *account_name, const char *player_name) {
449 
450  for (ac=accounts; ac; ac=ac->next) {
451  if (!strcasecmp(ac->name, account_name)) break;
452  }
453  if (ac == NULL) return 1;
454 
455  ensure_available_characters(ac, ac->num_characters + 1);
456  ac->character_names[ac->num_characters] = strdup_local(player_name);
457  ac->num_characters++;
458  return 0;
459 }
460 
479 int account_remove_player(const char *account_name, const char *player_name) {
481  int i, match=0;
482 
483  if (account_name == NULL)
484  return 0;
485 
486  for (ac=accounts; ac; ac=ac->next) {
487  if (!strcasecmp(ac->name, account_name)) break;
488  }
489  if (ac == NULL) return 1;
490 
491  /* Try to find the character name. Once we find it, we set match, and
492  * then move the remain character names down by one. The array is
493  * always null terminated, so this also makes sure we copy the null down.
494  */
495  for (i=0; i<ac->num_characters; i++) {
496  if (!strcmp(ac->character_names[i], player_name)) {
497  free(ac->character_names[i]);
498  match=1;
499  }
500  if (match == 1) {
501  ac->character_names[i] = ac->character_names[i+1];
502  }
503  }
504 
505  if (match) {
506  ac->num_characters--;
507  return 0;
508  }
509  /* Otherwise, did not find player name */
510  return 2;
511 }
512 
513 
525 char **account_get_players_for_account(const char *account_name)
526 {
528 
529  for (ac=accounts; ac; ac=ac->next) {
530  if (!strcasecmp(ac->name, account_name)) return ac->character_names;
531  }
532  return NULL;
533 }
534 
541 static int char_in_list(const char *name, const Account_Char *chars) {
542  while (chars) {
543  if (strcmp(chars->name, name) == 0) {
544  return 1;
545  }
546  chars = chars->next;
547  }
548  return 0;
549 }
550 
559 linked_char *account_get_additional_chars(const char *account_name, const Account_Char *chars, int *count) {
561  linked_char *ret = NULL;
562 
563  for (ac = accounts; ac; ac = ac->next) {
564  if (!strcasecmp(ac->name, account_name)) {
565  for (int i = 0; i < ac->num_characters; i++) {
566  if (!char_in_list(ac->character_names[i], chars)) {
567  linked_char *lc = (linked_char *)calloc(1, sizeof(linked_char));
568  lc->next = ret;
569  lc->name = ac->character_names[i];
570  ret = lc;
571  (*count)++;
572  }
573  }
574  return ret;
575  }
576  }
577  return NULL;
578 }
579 
589 const char *account_get_account_for_char(const char *charname)
590 {
592  int i;
593 
594  for (ac=accounts; ac; ac=ac->next) {
595  for (i=0; i<ac->num_characters; i++) {
596  if (!strcmp(ac->character_names[i], charname)) {
597  return ac->name;
598  }
599  }
600  }
601  return NULL;
602 
603 }
604 
615 {
616  player *pl;
617 
618  for (pl = first_player; pl; pl=pl->next) {
619  if (pl->socket.account_name &&
620  !strcasecmp(pl->socket.account_name, name)) return pl;
621  }
622  return NULL;
623 }
624 
636 {
637  int i;
638 
639  for (i=0; i < socket_info.allocated_sockets; i++) {
640  if (init_sockets[i].status == Ns_Add &&
641  init_sockets[i].account_name &&
642  !strcasecmp(init_sockets[i].account_name, name)) return(&init_sockets[i]);
643  }
644  return NULL;
645 }
646 
657 int account_is_logged_in(const char *name)
658 {
659  if (account_get_logged_in_player(name)) return 1;
660 
661  if (account_get_logged_in_init_socket(name)!=NULL) return 1;
662 
663  return 0;
664 }
665 
685 int account_change_password(const char *account_name,
686  const char *current_password, const char *new_password) {
688 
689  // Check password for invalid characters as in account_new().
690  if (account_check_string(account_name) ||
691  (current_password != NULL && account_check_string(current_password)) ||
692  account_check_string(new_password)) {
693  return 1;
694  }
695 
696  // Iterate through accounts list until a matching name is found.
697  for (ac = accounts; ac; ac = ac->next) {
698  if (!strcasecmp(ac->name, account_name)) {
699  break;
700  }
701  }
702 
703  // Check if the given account actually exists.
704  if (ac == NULL) {
705  return 2;
706  }
707 
708  // Return an error if the current password does not match.
709  if (current_password != NULL && !check_password(current_password, ac->password)) {
710  return 3;
711  }
712 
713  free(ac->password);
714  ac->password = strdup_local(newhash(new_password));
715 
716  return 0;
717 }
account_get_account_for_char
const char * account_get_account_for_char(const char *charname)
Definition: account.c:589
output_file.h
global.h
account_struct::name
char * name
Definition: account.c:76
account_struct
Definition: account.c:75
guildjoin.charname
def charname
Definition: guildjoin.py:45
accounts_clear
void accounts_clear(void)
Definition: account.c:148
llevError
@ llevError
Definition: logger.h:11
ensure_available_characters
static void ensure_available_characters(account_struct *account, int count)
Definition: account.c:111
init_sockets
socket_struct * init_sockets
Definition: init.c:58
strdup_local
#define strdup_local
Definition: compat.h:29
newhash
char const * newhash(char const *password)
bufferreader_init_from_file
BufferReader * bufferreader_init_from_file(BufferReader *br, const char *filepath, const char *failureMessage, LogLevel failureLevel)
Definition: bufferreader.c:65
socket_struct
Definition: newserver.h:89
Socket_Info::allocated_sockets
int allocated_sockets
Definition: newserver.h:144
NUM_ACCOUNT_FIELDS
#define NUM_ACCOUNT_FIELDS
Definition: account.c:62
pl::socket
socket_struct socket
Definition: player.h:94
pl
Definition: player.h:92
account_struct::created
time_t created
Definition: account.c:83
socket_info
Socket_Info socket_info
Definition: init.c:49
account_link
int account_link(const char *account_name, const char *player_name)
Definition: account.c:447
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.c:102
account_char_struct::name
sstring name
Definition: account_char.h:13
account_get_logged_in_player
player * account_get_logged_in_player(const char *name)
Definition: account.c:614
Ice.tmp
int tmp
Definition: Ice.py:207
account_struct::next
struct account_struct * next
Definition: account.c:84
curse_on_apply.ac
ac
Definition: curse_on_apply.py:4
account_struct::num_characters
int num_characters
Definition: account.c:79
accounts_load
void accounts_load(void)
Definition: account.c:157
account_char_struct
Definition: account_char.h:12
account_exists
const char * account_exists(const char *account_name)
Definition: account.c:302
linked_char
Definition: global.h:86
settings
struct Settings settings
Definition: init.c:39
account_password
void account_password(char *buf, int len, socket_struct *ns)
Definition: request.c:2994
account_alloc
static account_struct * account_alloc()
Definition: account.c:131
pl::next
struct pl * next
Definition: player.h:93
linked_char::name
const char * name
Definition: global.h:87
of_close
int of_close(OutputFile *of)
Definition: output_file.c:61
account_struct::last_login
time_t last_login
Definition: account.c:78
fatal
void fatal(enum fatal_error err)
Definition: utils.c:597
make_face_from_files.str
str
Definition: make_face_from_files.py:24
account_login
int account_login(const char *account_name, const char *account_password)
Definition: account.c:326
ACCOUNT_FILE
#define ACCOUNT_FILE
Definition: account.c:104
socket_struct::account_name
char * account_name
Definition: newserver.h:126
first_player
EXTERN player * first_player
Definition: global.h:115
linked_char::next
struct linked_char * next
Definition: global.h:88
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
account_char_struct::next
struct account_char_struct * next
Definition: account_char.h:21
account_remove_player
int account_remove_player(const char *account_name, const char *player_name)
Definition: account.c:479
sproto.h
account_get_logged_in_init_socket
socket_struct * account_get_logged_in_init_socket(const char *name)
Definition: account.c:635
MAX_BUF
#define MAX_BUF
Definition: define.h:35
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.c:500
Ns_Add
@ Ns_Add
Definition: newserver.h:66
accounts_loaded
static int accounts_loaded
Definition: account.c:98
account_get_players_for_account
char ** account_get_players_for_account(const char *account_name)
Definition: account.c:525
llevInfo
@ llevInfo
Definition: logger.h:12
account_change_password
int account_change_password(const char *account_name, const char *current_password, const char *new_password)
Definition: account.c:685
account_struct::allocated_characters
int allocated_characters
Definition: account.c:80
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
account_struct
struct account_struct account_struct
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.c:30
account_get_additional_chars
linked_char * account_get_additional_chars(const char *account_name, const Account_Char *chars, int *count)
Definition: account.c:559
bufferreader_destroy
void bufferreader_destroy(BufferReader *br)
Definition: bufferreader.c:40
account_new
int account_new(const char *account_name, const char *account_password)
Definition: account.c:401
buf
StringBuffer * buf
Definition: readable.c:1606
accounts_save
void accounts_save(void)
Definition: account.c:260
strcasecmp
int strcasecmp(const char *s1, const char *s2)
account_is_logged_in
int account_is_logged_in(const char *name)
Definition: account.c:657
account_check_string
int account_check_string(const char *str)
Definition: account.c:362
check_password
bool check_password(const char *typed, const char *crypted)
Definition: server.c:114
char_in_list
static int char_in_list(const char *name, const Account_Char *chars)
Definition: account.c:541
account_struct::character_names
char ** character_names
Definition: account.c:81
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
BufferReader
Definition: bufferreader.c:21
takeitem.status
status
Definition: takeitem.py:35
account_write_entry
static void account_write_entry(FILE *fp, account_struct *ac)
Definition: account.c:239
object.h
accounts
static account_struct * accounts
Definition: account.c:90
give.name
name
Definition: give.py:27
account_struct::password
char * password
Definition: account.c:77
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Definition: global.h:245