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 f
= fopen(USERS_FILE
, "r+");
127 write_log (LOG_ERROR
, "error opening " USERS_FILE
);
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
);
139 // user already exists
140 if (find_user(name
, usr
, f
, &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
);
170 int check_user_login (user
*usr
, const char *name
, const char *pass
)
176 f
= fopen(USERS_FILE
, "r+"); // r+ is needed for locking
179 write_log (LOG_ERROR
, "error opening " USERS_FILE
);
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
);
191 id
= find_user(name
, usr
, f
, NULL
);
194 if (id
<= 0) return -4;
196 if (strcmp(pass
, usr
->pass
) != 0) return -5;
203 static void change_password_save (FILE *f
, char *buf
, int size
, int user_id
, const char *pass
)
206 char *end
= buf
+ size
;
212 char *s
= strstr(buf
, "\n");
214 if (s
== NULL
) s
= end
;
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
229 strncpy (usr
.pass
, pass
, USER_PASS_LEN
);
230 usr
.pass
[USER_PASS_LEN
- 1] = 0;
231 write_user (f
, &usr
);
240 int change_password (int user_id
, const char *pass
)
247 f
= fopen(USERS_FILE
, "r+");
250 write_log (LOG_ERROR
, "error opening " USERS_FILE
);
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
);
262 // allocate buffer to store all users
264 if (fstat(fileno(f
), &st
) == -1)
266 write_log (LOG_ERROR
, "can't stat " USERS_FILE
);
271 buf
= (char*)malloc(st
.st_size
);
274 write_log (LOG_ERROR
, "unable to allocate memory to load " USERS_FILE
);
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
);