10 #define USERS_FILE "./users"
13 static int check_user_name (const char *name
)
18 if (!isalpha(name
[0])) return 0;
20 for (i
= 1; name
[i
] != 0; i
++)
22 if (!isalnum(name
[i
])) return 0;
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
)
38 if ((s
= strstr(str
, ":")) == NULL
) return 0;
45 if ((s
= strstr(str
, ":")) == NULL
) return 0;
49 strncpy (usr
->name
, str
, USER_NAME_LEN
);
50 usr
->name
[USER_NAME_LEN
- 1] = 0;
53 if ((s
= strstr(str
, ":")) == NULL
) return 0;
57 strncpy (usr
->pass
, str
, USER_PASS_LEN
);
58 usr
->name
[USER_PASS_LEN
- 1] = 0;
61 if ((s
= strstr(str
, "\n")) != NULL
) *s
= 0; // remove optional \n at the end
64 strncpy (usr
->email
, str
, USER_EMAIL_LEN
);
65 usr
->name
[USER_EMAIL_LEN
- 1] = 0;
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
)
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
))
97 // logins are case-insensitive
98 if (strcasecmp(name
, usr
->name
) == 0) return usr
->id
;
101 if (usr
->id
> m
) m
= usr
->id
;
104 if (max_id
!= NULL
) *max_id
= m
;
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
)
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
);
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
);
138 // user already exists
139 if (find_user(name
, usr
, f
, &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
);
169 int check_user_login (user
*usr
, const char *name
, const char *pass
)
175 f
= fopen(USERS_FILE
, "r+"); // r+ is needed for locking
178 write_log (LOG_ERROR
, "error opening " USERS_FILE
);
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
);
190 id
= find_user(name
, usr
, f
, NULL
);
193 if (id
<= 0) return -4;
195 if (strcmp(pass
, usr
->pass
) != 0) return -5;
202 static void change_password_save (FILE *f
, char *buf
, int size
, int user_id
, const char *pass
)
205 char *end
= buf
+ size
;
211 char *s
= strstr(buf
, "\n");
213 if (s
== NULL
) s
= end
;
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
228 strncpy (usr
.pass
, pass
, USER_PASS_LEN
);
229 usr
.pass
[USER_PASS_LEN
- 1] = 0;
230 write_user (f
, &usr
);
239 int change_password (int user_id
, const char *pass
)
246 if ((f
= fopen(USERS_FILE
, "r+")) == NULL
)
248 write_log (LOG_ERROR
, "error opening " USERS_FILE
);
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
);
260 // allocate buffer to store all users
262 if (fstat(fileno(f
), &st
) == -1)
264 write_log (LOG_ERROR
, "can't stat " USERS_FILE
);
269 buf
= (char*)malloc(st
.st_size
);
272 write_log (LOG_ERROR
, "unable to allocate memory to load " USERS_FILE
);
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
);