/[LeafOK_CVS]/lbbs/src/login.c
ViewVC logotype

Contents of /lbbs/src/login.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.62 - (show annotations)
Wed Oct 22 16:12:50 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.61: +1 -1 lines
Content type: text/x-csrc
Add data fields of user

1 /***************************************************************************
2 login.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "bbs.h"
18 #include "common.h"
19 #include "database.h"
20 #include "io.h"
21 #include "ip_mask.h"
22 #include "log.h"
23 #include "login.h"
24 #include "screen.h"
25 #include "user_priv.h"
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <regex.h>
31 #include <unistd.h>
32 #include <mysql/mysql.h>
33 #include <sys/param.h>
34
35 int bbs_login(void)
36 {
37 char username[BBS_username_max_len + 1];
38 char password[BBS_password_max_len + 1];
39 int i = 0;
40 int ok = 0;
41
42 for (; !SYS_server_exit && !ok && i < BBS_login_retry_times; i++)
43 {
44 prints("\033[1;33m请输入帐号\033[m(试用请输入`\033[1;36mguest\033[m', "
45 "注册请输入`\033[1;31mnew\033[m'): ");
46 iflush();
47
48 if (str_input(username, sizeof(username), DOECHO) < 0)
49 {
50 continue;
51 }
52
53 if (strcmp(username, "guest") == 0)
54 {
55 load_guest_info();
56
57 return 0;
58 }
59
60 if (strcmp(username, "new") == 0)
61 {
62 display_file(DATA_REGISTER, 1);
63
64 return -1;
65 }
66
67 if (username[0] != '\0')
68 {
69 prints("\033[1;37m请输入密码\033[m: ");
70 iflush();
71
72 if (str_input(password, sizeof(password), NOECHO) < 0)
73 {
74 continue;
75 }
76
77 ok = (check_user(username, password) == 0);
78 iflush();
79 }
80 }
81
82 if (!ok)
83 {
84 display_file(DATA_LOGIN_ERROR, 1);
85 return -1;
86 }
87
88 log_common("User \"%s\"(%ld) login from %s:%d\n",
89 BBS_username, BBS_priv.uid, hostaddr_client, port_client);
90
91 return 0;
92 }
93
94 int check_user(const char *username, const char *password)
95 {
96 MYSQL *db = NULL;
97 MYSQL_RES *rs = NULL;
98 MYSQL_ROW row;
99 char sql[SQL_BUFFER_LEN];
100 int ret = 0;
101 int BBS_uid = 0;
102 char client_addr[IP_ADDR_LEN];
103 int i;
104 int ok = 1;
105 char user_tz_env[BBS_user_tz_max_len + 2];
106
107 db = db_open();
108 if (db == NULL)
109 {
110 ret = -1;
111 goto cleanup;
112 }
113
114 // Verify format
115 for (i = 0; ok && username[i] != '\0'; i++)
116 {
117 if (!(isalpha(username[i]) || (i > 0 && isdigit(username[i]))))
118 {
119 ok = 0;
120 }
121 }
122 if (ok && (i < 3 || i > 12))
123 {
124 ok = 0;
125 }
126 for (i = 0; ok && password[i] != '\0'; i++)
127 {
128 if (!isalnum(password[i]))
129 {
130 ok = 0;
131 }
132 }
133 if (ok && (i < 5 || i > 12))
134 {
135 ok = 0;
136 }
137
138 if (!ok)
139 {
140 prints("\033[1;31m用户名或密码格式错误...\033[m\r\n");
141 ret = 1;
142 goto cleanup;
143 }
144
145 // Begin transaction
146 if (mysql_query(db, "SET autocommit=0") != 0)
147 {
148 log_error("SET autocommit=0 error: %s\n", mysql_error(db));
149 ret = -1;
150 goto cleanup;
151 }
152
153 if (mysql_query(db, "BEGIN") != 0)
154 {
155 log_error("Begin transaction error: %s\n", mysql_error(db));
156 ret = -1;
157 goto cleanup;
158 }
159
160 // Failed login attempts from the same source (subnet /24) during certain time period
161 strncpy(client_addr, hostaddr_client, sizeof(client_addr) - 1);
162 client_addr[sizeof(client_addr) - 1] = '\0';
163
164 snprintf(sql, sizeof(sql),
165 "SELECT COUNT(*) AS err_count FROM user_err_login_log "
166 "WHERE login_dt >= SUBDATE(NOW(), INTERVAL %d MINUTE) "
167 "AND login_ip LIKE '%s'",
168 BBS_login_failures_count_interval,
169 ip_mask(client_addr, 1, '%'));
170 if (mysql_query(db, sql) != 0)
171 {
172 log_error("Query user_list error: %s\n", mysql_error(db));
173 ret = -1;
174 goto cleanup;
175 }
176 if ((rs = mysql_store_result(db)) == NULL)
177 {
178 log_error("Get user_list data failed\n");
179 ret = -1;
180 goto cleanup;
181 }
182 if ((row = mysql_fetch_row(rs)))
183 {
184 if (atoi(row[0]) >= BBS_allowed_login_failures_within_interval)
185 {
186 prints("\033[1;31m来源存在多次失败登陆尝试,请稍后再试,或使用Web方式访问\033[m\r\n");
187 ret = 1;
188 goto cleanup;
189 }
190 }
191 mysql_free_result(rs);
192 rs = NULL;
193
194 // Failed login attempts against the current username since last successful login
195 snprintf(sql, sizeof(sql),
196 "SELECT COUNT(*) AS err_count FROM user_err_login_log "
197 "LEFT JOIN user_list ON user_err_login_log.username = user_list.username "
198 "LEFT JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
199 "WHERE user_err_login_log.username = '%s' "
200 "AND (user_err_login_log.login_dt >= user_pubinfo.last_login_dt "
201 "OR user_pubinfo.last_login_dt IS NULL)",
202 username);
203 if (mysql_query(db, sql) != 0)
204 {
205 log_error("Query user_list error: %s\n", mysql_error(db));
206 ret = -1;
207 goto cleanup;
208 }
209 if ((rs = mysql_store_result(db)) == NULL)
210 {
211 log_error("Get user_list data failed\n");
212 ret = -1;
213 goto cleanup;
214 }
215 if ((row = mysql_fetch_row(rs)))
216 {
217 if (atoi(row[0]) >= BBS_allowed_login_failures_per_account)
218 {
219 prints("\033[1;31m账户存在多次失败登陆尝试,请使用Web方式登录解锁\033[m\r\n");
220 ret = 1;
221 goto cleanup;
222 }
223 }
224 mysql_free_result(rs);
225 rs = NULL;
226
227 snprintf(sql, sizeof(sql),
228 "SELECT UID, username, p_login FROM user_list "
229 "WHERE username = '%s' AND password = SHA2('%s', 256) AND enable",
230 username, password);
231 if (mysql_query(db, sql) != 0)
232 {
233 log_error("Query user_list error: %s\n", mysql_error(db));
234 ret = -1;
235 goto cleanup;
236 }
237 if ((rs = mysql_store_result(db)) == NULL)
238 {
239 log_error("Get user_list data failed\n");
240 ret = -1;
241 goto cleanup;
242 }
243 if ((row = mysql_fetch_row(rs)))
244 {
245 BBS_uid = atoi(row[0]);
246 strncpy(BBS_username, row[1], sizeof(BBS_username) - 1);
247 BBS_username[sizeof(BBS_username) - 1] = '\0';
248 int p_login = atoi(row[2]);
249
250 mysql_free_result(rs);
251 rs = NULL;
252
253 // Add user login log
254 snprintf(sql, sizeof(sql),
255 "INSERT INTO user_login_log(UID, login_dt, login_ip) "
256 "VALUES(%d, NOW(), '%s')",
257 BBS_uid, hostaddr_client);
258 if (mysql_query(db, sql) != 0)
259 {
260 log_error("Insert into user_login_log error: %s\n", mysql_error(db));
261 ret = -1;
262 goto cleanup;
263 }
264
265 // Commit transaction
266 if (mysql_query(db, "COMMIT") != 0)
267 {
268 log_error("Commit transaction error: %s\n", mysql_error(db));
269 ret = -1;
270 goto cleanup;
271 }
272
273 if (p_login == 0)
274 {
275 prints("\033[1;31m您目前无权登陆...\033[m\r\n");
276 ret = 1;
277 goto cleanup;
278 }
279 }
280 else
281 {
282 mysql_free_result(rs);
283 rs = NULL;
284
285 snprintf(sql, sizeof(sql),
286 "INSERT INTO user_err_login_log(username, password, login_dt, login_ip) "
287 "VALUES('%s', '%s', NOW(), '%s')",
288 username, password, hostaddr_client);
289 if (mysql_query(db, sql) != 0)
290 {
291 log_error("Insert into user_err_login_log error: %s\n", mysql_error(db));
292 ret = -1;
293 goto cleanup;
294 }
295
296 // Commit transaction
297 if (mysql_query(db, "COMMIT") != 0)
298 {
299 log_error("Commit transaction error: %s\n", mysql_error(db));
300 ret = -1;
301 goto cleanup;
302 }
303
304 prints("\033[1;31m错误的用户名或密码...\033[m\r\n");
305 ret = 1;
306 goto cleanup;
307 }
308
309 // Set AUTOCOMMIT = 1
310 if (mysql_query(db, "SET autocommit=1") != 0)
311 {
312 log_error("SET autocommit=1 error: %s\n", mysql_error(db));
313 ret = -1;
314 goto cleanup;
315 }
316
317 ret = load_user_info(db, BBS_uid);
318
319 switch (ret)
320 {
321 case 0: // Login successfully
322 break;
323 case -1: // Load data error
324 prints("\033[1;31m读取用户数据错误...\033[m\r\n");
325 ret = -1;
326 goto cleanup;
327 case -2: // Unused
328 prints("\033[1;31m请通过Web登录更新用户许可协议...\033[m\r\n");
329 ret = 1;
330 goto cleanup;
331 case -3: // Dead
332 prints("\033[1;31m很遗憾,您已经永远离开了我们的世界!\033[m\r\n");
333 ret = 1;
334 goto cleanup;
335 default:
336 ret = -2;
337 goto cleanup;
338 }
339
340 snprintf(sql, sizeof(sql),
341 "UPDATE user_pubinfo SET visit_count = visit_count + 1, "
342 "last_login_dt = NOW() WHERE UID = %d",
343 BBS_uid);
344 if (mysql_query(db, sql) != 0)
345 {
346 log_error("Update user_pubinfo error: %s\n", mysql_error(db));
347 ret = -1;
348 goto cleanup;
349 }
350
351 if (user_online_add(db) != 0)
352 {
353 ret = -1;
354 goto cleanup;
355 }
356
357 BBS_last_access_tm = BBS_login_tm = time(NULL);
358
359 // Set user tz to process env
360 if (BBS_user_tz[0] != '\0')
361 {
362 user_tz_env[0] = ':';
363 strncpy(user_tz_env + 1, BBS_user_tz, sizeof(user_tz_env) - 2);
364 user_tz_env[sizeof(user_tz_env) - 1] = '\0';
365
366 if (setenv("TZ", user_tz_env, 1) == -1)
367 {
368 log_error("setenv(TZ = %s) error %d\n", user_tz_env, errno);
369 return -3;
370 }
371
372 tzset();
373 }
374
375 cleanup:
376 mysql_free_result(rs);
377 mysql_close(db);
378
379 return ret;
380 }
381
382 int load_user_info(MYSQL *db, int BBS_uid)
383 {
384 MYSQL_RES *rs = NULL;
385 MYSQL_ROW row;
386 char sql[SQL_BUFFER_LEN];
387 int life;
388 time_t last_login_dt;
389 int ret = 0;
390
391 snprintf(sql, sizeof(sql),
392 "SELECT life, UNIX_TIMESTAMP(last_login_dt), user_timezone, exp, nickname "
393 "FROM user_pubinfo WHERE UID = %d",
394 BBS_uid);
395 if (mysql_query(db, sql) != 0)
396 {
397 log_error("Query user_pubinfo error: %s\n", mysql_error(db));
398 ret = -1;
399 goto cleanup;
400 }
401 if ((rs = mysql_store_result(db)) == NULL)
402 {
403 log_error("Get user_pubinfo data failed\n");
404 ret = -1;
405 goto cleanup;
406 }
407 if ((row = mysql_fetch_row(rs)))
408 {
409 life = atoi(row[0]);
410 last_login_dt = (time_t)atol(row[1]);
411
412 strncpy(BBS_user_tz, row[2], sizeof(BBS_user_tz) - 1);
413 BBS_user_tz[sizeof(BBS_user_tz) - 1] = '\0';
414
415 BBS_user_exp = atoi(row[3]);
416
417 strncpy(BBS_nickname, row[4], sizeof(BBS_nickname));
418 BBS_nickname[sizeof(BBS_nickname) - 1] = '\0';
419 }
420 else
421 {
422 ret = -1; // Data not found
423 goto cleanup;
424 }
425 mysql_free_result(rs);
426 rs = NULL;
427
428 if (life != 333 && life != 365 && life != 666 && life != 999 && // Not immortal
429 time(NULL) - last_login_dt > 60 * 60 * 24 * life)
430 {
431 ret = -3; // Dead
432 goto cleanup;
433 }
434
435 if (load_priv(db, &BBS_priv, BBS_uid) != 0)
436 {
437 ret = -1;
438 goto cleanup;
439 }
440
441 cleanup:
442 mysql_free_result(rs);
443
444 return ret;
445 }
446
447 int load_guest_info(void)
448 {
449 MYSQL *db = NULL;
450 int ret = 0;
451
452 db = db_open();
453 if (db == NULL)
454 {
455 ret = -1;
456 goto cleanup;
457 }
458
459 strncpy(BBS_username, "guest", sizeof(BBS_username) - 1);
460 BBS_username[sizeof(BBS_username) - 1] = '\0';
461
462 BBS_user_exp = 0;
463
464 strncpy(BBS_nickname, "Guest", sizeof(BBS_nickname));
465 BBS_nickname[sizeof(BBS_nickname) - 1] = '\0';
466
467 if (load_priv(db, &BBS_priv, 0) != 0)
468 {
469 ret = -1;
470 goto cleanup;
471 }
472
473 if (user_online_add(db) != 0)
474 {
475 ret = -1;
476 goto cleanup;
477 }
478
479 BBS_last_access_tm = BBS_login_tm = time(NULL);
480
481 cleanup:
482 mysql_close(db);
483
484 return ret;
485 }
486
487 int user_online_add(MYSQL *db)
488 {
489 char sql[SQL_BUFFER_LEN];
490
491 snprintf(sql, sizeof(sql),
492 "INSERT INTO visit_log(dt, IP) VALUES(NOW(), '%s')",
493 hostaddr_client);
494 if (mysql_query(db, sql) != 0)
495 {
496 log_error("Add visit log error: %s\n", mysql_error(db));
497 return -1;
498 }
499
500 if (user_online_del(db) != 0)
501 {
502 return -2;
503 }
504
505 snprintf(sql, sizeof(sql),
506 "INSERT INTO user_online(SID, UID, ip, current_action, login_tm, last_tm) "
507 "VALUES('Telnet_Process_%d', %d, '%s', 'LOGIN', NOW(), NOW())",
508 getpid(), BBS_priv.uid, hostaddr_client);
509 if (mysql_query(db, sql) != 0)
510 {
511 log_error("Add user_online error: %s\n", mysql_error(db));
512 return -3;
513 }
514
515 return 0;
516 }
517
518 int user_online_del(MYSQL *db)
519 {
520 char sql[SQL_BUFFER_LEN];
521
522 snprintf(sql, sizeof(sql),
523 "DELETE FROM user_online WHERE SID = 'Telnet_Process_%d'",
524 getpid());
525 if (mysql_query(db, sql) != 0)
526 {
527 log_error("Delete user_online error: %s\n", mysql_error(db));
528 return -1;
529 }
530
531 return 0;
532 }
533
534 int user_online_exp(MYSQL *db)
535 {
536 char sql[SQL_BUFFER_LEN];
537
538 // +1 exp for every 5 minutes online since last logout
539 // but at most 24 hours worth of exp can be gained in Telnet session
540 snprintf(sql, sizeof(sql),
541 "UPDATE user_pubinfo SET exp = exp + FLOOR(LEAST(TIMESTAMPDIFF("
542 "SECOND, GREATEST(last_login_dt, IF(last_logout_dt IS NULL, last_login_dt, last_logout_dt)), NOW()"
543 ") / 60 / 5, 12 * 24)), last_logout_dt = NOW() "
544 "WHERE UID = %d",
545 BBS_priv.uid);
546 if (mysql_query(db, sql) != 0)
547 {
548 log_error("Update user_pubinfo error: %s\n", mysql_error(db));
549 return -1;
550 }
551
552 return 0;
553 }
554
555 int user_online_update(const char *action)
556 {
557 MYSQL *db = NULL;
558 char sql[SQL_BUFFER_LEN];
559
560 if ((action == NULL || strcmp(BBS_current_action, action) == 0) &&
561 time(NULL) - BBS_current_action_tm < BBS_current_action_refresh_interval) // No change
562 {
563 return 0;
564 }
565
566 if (action != NULL)
567 {
568 strncpy(BBS_current_action, action, sizeof(BBS_current_action) - 1);
569 BBS_current_action[sizeof(BBS_current_action) - 1] = '\0';
570 }
571
572 BBS_current_action_tm = time(NULL);
573
574 db = db_open();
575 if (db == NULL)
576 {
577 log_error("db_open() error: %s\n", mysql_error(db));
578 return -1;
579 }
580
581 snprintf(sql, sizeof(sql),
582 "UPDATE user_online SET current_action = '%s', last_tm = NOW() "
583 "WHERE SID = 'Telnet_Process_%d'",
584 BBS_current_action, getpid());
585 if (mysql_query(db, sql) != 0)
586 {
587 log_error("Update user_online error: %s\n", mysql_error(db));
588 return -2;
589 }
590
591 mysql_close(db);
592
593 return 1;
594 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1