updated docs
[rlserver.git] / users.c
blob544b4d1e34d0778d3e97cf91bee63772dcf9c4dd
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 f = fopen(USERS_FILE, "r+");
125 if (f == NULL)
127 write_log (LOG_ERROR, "error opening " USERS_FILE);
128 return -2;
131 // lock file to block access from other threads
132 if (lockf(fileno(f), F_LOCK, 0) != 0)
134 write_log (LOG_ERROR, "error locking " USERS_FILE);
135 fclose (f);
136 return -3;
139 // user already exists
140 if (find_user(name, usr, f, &id))
142 fclose (f);
143 return -4;
146 id++;
147 usr->id = id;
149 strncpy (usr->name, name, USER_NAME_LEN);
150 usr->name[USER_NAME_LEN - 1] = 0;
152 strncpy (usr->pass, pass, USER_PASS_LEN);
153 usr->pass[USER_PASS_LEN - 1] = 0;
155 strncpy (usr->email, email, USER_EMAIL_LEN);
156 usr->email[USER_EMAIL_LEN - 1] = 0;
158 // add user record to the file
159 fseek (f, 0, SEEK_END);
161 write_user (f, usr);
163 fclose (f);
165 return id;
170 int check_user_login (user *usr, const char *name, const char *pass)
172 int id;
173 FILE *f;
176 f = fopen(USERS_FILE, "r+"); // r+ is needed for locking
177 if (f == NULL)
179 write_log (LOG_ERROR, "error opening " USERS_FILE);
180 return -2;
183 // lock file to block access from other threads
184 if (lockf(fileno(f), F_LOCK, 0) != 0)
186 write_log (LOG_ERROR, "error locking " USERS_FILE);
187 fclose (f);
188 return -3;
191 id = find_user(name, usr, f, NULL);
192 fclose (f);
194 if (id <= 0) return -4;
196 if (strcmp(pass, usr->pass) != 0) return -5;
198 return id;
203 static void change_password_save (FILE *f, char *buf, int size, int user_id, const char *pass)
205 char str[BUF_SZ];
206 char *end = buf + size;
209 while (buf <= end)
211 user usr;
212 char *s = strstr(buf, "\n");
214 if (s == NULL) s = end;
215 else *s = 0;
217 // copy string (parse_user is destructive)
218 snprintf (str, BUF_SZ, buf);
220 // not user or not this user
221 // just copy the string
222 if (!parse_user(&usr, str) || usr.id != user_id)
224 fprintf (f, "%s\n", buf);
226 // replace password and save user
227 else
229 strncpy (usr.pass, pass, USER_PASS_LEN);
230 usr.pass[USER_PASS_LEN - 1] = 0;
231 write_user (f, &usr);
234 buf = s + 1;
240 int change_password (int user_id, const char *pass)
242 struct stat st;
243 char *buf;
244 FILE *f;
247 f = fopen(USERS_FILE, "r+");
248 if (f == NULL)
250 write_log (LOG_ERROR, "error opening " USERS_FILE);
251 return -2;
254 // lock file to block access from other threads
255 if (lockf(fileno(f), F_LOCK, 0) != 0)
257 write_log (LOG_ERROR, "error locking " USERS_FILE);
258 fclose (f);
259 return -3;
262 // allocate buffer to store all users
264 if (fstat(fileno(f), &st) == -1)
266 write_log (LOG_ERROR, "can't stat " USERS_FILE);
267 fclose (f);
268 return -4;
271 buf = (char*)malloc(st.st_size);
272 if (buf == NULL)
274 write_log (LOG_ERROR, "unable to allocate memory to load " USERS_FILE);
275 fclose (f);
276 return -5;
279 // read the whole user file into memory
280 fread (buf, st.st_size, 1, f);
282 fseek (f, 0, SEEK_SET);
283 change_password_save (f, buf, st.st_size, user_id, pass);
285 fclose (f);
286 free (buf);
288 return 0;