updated docs
[rlserver.git] / users.c
blob2142800caa515b42e01de7546cd3600277abb467
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include "log.h"
8 #include "users.h"
10 #define USERS_FILE "./users"
13 static int check_user_name (const char *name)
15 int i;
18 if (!isalpha(name[0])) return 0;
20 for (i = 1; name[i] != 0; i++)
22 if (!isalnum(name[i])) return 0;
25 // minimal length
26 return i >= 3;
32 * str must be 0-terminated
33 * NB! content of str will be destroyed in the process
35 static int parse_user (user *usr, char *str)
37 char *s;
38 if ((s = strstr(str, ":")) == NULL) return 0;
39 *s = 0;
41 // id
42 usr->id = atoi(str);
44 str = s + 1;
45 if ((s = strstr(str, ":")) == NULL) return 0;
46 *s = 0;
48 // name
49 strncpy (usr->name, str, USER_NAME_LEN);
50 usr->name[USER_NAME_LEN - 1] = 0;
52 str = s + 1;
53 if ((s = strstr(str, ":")) == NULL) return 0;
54 *s = 0;
56 // password
57 strncpy (usr->pass, str, USER_PASS_LEN);
58 usr->name[USER_PASS_LEN - 1] = 0;
60 str = s + 1;
61 if ((s = strstr(str, "\n")) != NULL) *s = 0; // remove optional \n at the end
63 // email
64 strncpy (usr->email, str, USER_EMAIL_LEN);
65 usr->name[USER_EMAIL_LEN - 1] = 0;
67 return 1;
72 #define BUF_SZ 1024
75 * if user is not found usr will be filled with garbage
76 * and also if max_id is not NULL, it will contain max encountered user id
78 static int find_user (const char *name, user *usr, FILE *f, int *max_id)
80 int m = 0;
82 while (1)
84 char buf[BUF_SZ];
86 // end of file
87 if (fgets(buf, BUF_SZ, f) == NULL) break;
89 // skip comments and empty lines
90 if (buf[0] == '#' || buf[0] == 0) continue;
92 if (!parse_user(usr, buf))
94 continue;
97 // logins are case-insensitive
98 if (strcasecmp(name, usr->name) == 0) return usr->id;
100 // update max id
101 if (usr->id > m) m = usr->id;
104 if (max_id != NULL) *max_id = m;
105 return 0;
110 static void write_user (FILE *f, const user *usr)
112 fprintf (f, "%d:%s:%s:%s\n", usr->id, usr->name, usr->pass, usr->email);
117 int add_user (user *usr, const char *name, const char *pass, const char *email)
119 int id;
120 FILE *f;
122 if (!check_user_name(name)) return -1;
124 if ((f = fopen(USERS_FILE, "r+")) == NULL)
126 write_log (LOG_ERROR, "error opening " USERS_FILE);
127 return -2;
130 // lock file to block access from other threads
131 if (lockf(fileno(f), F_LOCK, 0) != 0)
133 write_log (LOG_ERROR, "error locking " USERS_FILE);
134 fclose (f);
135 return -3;
138 // user already exists
139 if (find_user(name, usr, f, &id))
141 fclose (f);
142 return -4;
145 id++;
146 usr->id = id;
148 strncpy (usr->name, name, USER_NAME_LEN);
149 usr->name[USER_NAME_LEN - 1] = 0;
151 strncpy (usr->pass, pass, USER_PASS_LEN);
152 usr->pass[USER_PASS_LEN - 1] = 0;
154 strncpy (usr->email, email, USER_EMAIL_LEN);
155 usr->email[USER_EMAIL_LEN - 1] = 0;
157 // add user record to the file
158 fseek (f, 0, SEEK_END);
160 write_user (f, usr);
162 fclose (f);
164 return id;
169 int check_user_login (user *usr, const char *name, const char *pass)
171 int id;
172 FILE *f;
175 f = fopen(USERS_FILE, "r+"); // r+ is needed for locking
176 if (f == NULL)
178 write_log (LOG_ERROR, "error opening " USERS_FILE);
179 return -2;
182 // lock file to block access from other threads
183 if (lockf(fileno(f), F_LOCK, 0) != 0)
185 write_log (LOG_ERROR, "error locking " USERS_FILE);
186 fclose (f);
187 return -3;
190 id = find_user(name, usr, f, NULL);
191 fclose (f);
193 if (id <= 0) return -4;
195 if (strcmp(pass, usr->pass) != 0) return -5;
197 return id;
202 static void change_password_save (FILE *f, char *buf, int size, int user_id, const char *pass)
204 char str[BUF_SZ];
205 char *end = buf + size;
208 while (buf <= end)
210 user usr;
211 char *s = strstr(buf, "\n");
213 if (s == NULL) s = end;
214 else *s = 0;
216 // copy string (parse_user is destructive)
217 snprintf (str, BUF_SZ, buf);
219 // not user or not this user
220 // just copy the string
221 if (!parse_user(&usr, str) || usr.id != user_id)
223 fprintf (f, "%s\n", buf);
225 // replace password and save user
226 else
228 strncpy (usr.pass, pass, USER_PASS_LEN);
229 usr.pass[USER_PASS_LEN - 1] = 0;
230 write_user (f, &usr);
233 buf = s + 1;
239 int change_password (int user_id, const char *pass)
241 struct stat st;
242 char *buf;
243 FILE *f;
246 if ((f = fopen(USERS_FILE, "r+")) == NULL)
248 write_log (LOG_ERROR, "error opening " USERS_FILE);
249 return -2;
252 // lock file to block access from other threads
253 if (lockf(fileno(f), F_LOCK, 0) != 0)
255 write_log (LOG_ERROR, "error locking " USERS_FILE);
256 fclose (f);
257 return -3;
260 // allocate buffer to store all users
262 if (fstat(fileno(f), &st) == -1)
264 write_log (LOG_ERROR, "can't stat " USERS_FILE);
265 fclose (f);
266 return -4;
269 buf = (char*)malloc(st.st_size);
270 if (buf == NULL)
272 write_log (LOG_ERROR, "unable to allocate memory to load " USERS_FILE);
273 fclose (f);
274 return -5;
277 // read the whole user file into memory
278 fread (buf, st.st_size, 1, f);
280 fseek (f, 0, SEEK_SET);
281 change_password_save (f, buf, st.st_size, user_id, pass);
283 fclose (f);
284 free (buf);
286 return 0;