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

Contents of /lbbs/src/login.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.49 - (show annotations)
Wed Jul 2 04:17:33 2025 UTC (8 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.48: +11 -11 lines
Content type: text/x-csrc
Support UTF8 instead of GBK

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

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