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

Contents of /lbbs/src/user_list_display.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (show annotations)
Mon Nov 3 06:21:01 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.16: +2 -2 lines
Content type: text/x-csrc
Update username check criteria

1 /***************************************************************************
2 user_list_display.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 "common.h"
18 #include "io.h"
19 #include "log.h"
20 #include "login.h"
21 #include "screen.h"
22 #include "str_process.h"
23 #include "user_list.h"
24 #include "user_priv.h"
25 #include "user_info_display.h"
26 #include "user_list_display.h"
27 #include <ctype.h>
28 #include <string.h>
29 #include <time.h>
30 #include <sys/param.h>
31
32 enum select_cmd_t
33 {
34 EXIT_LIST = 0,
35 VIEW_USER,
36 CHANGE_PAGE,
37 REFRESH_LIST,
38 SHOW_HELP,
39 SEARCH_USER,
40 };
41
42 static int user_list_draw_screen(int online_user)
43 {
44 clearscr();
45 show_top((online_user ? "[线上使用者]" : "[已注册用户]"), BBS_name, "");
46 moveto(2, 0);
47 prints("返回[\033[1;32m←\033[0;37m,\033[1;32mESC\033[0;37m] 选择[\033[1;32m↑\033[0;37m,\033[1;32m↓\033[0;37m] "
48 "查看[\033[1;32m→\033[0;37m,\033[1;32mENTER\033[0;37m] 查找[\033[1;32ms\033[0;37m] "
49 "帮助[\033[1;32mh\033[0;37m]\033[m");
50 moveto(3, 0);
51
52 if (online_user)
53 {
54 prints("\033[44;37m \033[1;37m 编 号 用户名 昵称 在线时长 空闲 最后活动 \033[m");
55 }
56 else
57 {
58 prints("\033[44;37m \033[1;37m 编 号 用户名 昵称 上次登陆距今 \033[m");
59 }
60
61 return 0;
62 }
63
64 static int user_list_draw_items(int page_id, USER_INFO *p_users, int user_count)
65 {
66 char str_time_login[LINE_BUFFER_LEN];
67 time_t tm_now;
68 time_t tm_duration;
69 struct tm *p_tm;
70 int i;
71
72 clrline(4, 23);
73
74 time(&tm_now);
75
76 for (i = 0; i < user_count; i++)
77 {
78 tm_duration = tm_now - p_users[i].last_login_dt;
79 p_tm = gmtime(&tm_duration);
80 if (p_tm == NULL)
81 {
82 log_error("Invalid time duration\n");
83 str_time_login[0] = '\0';
84 }
85 else if (p_tm->tm_year > 70)
86 {
87 snprintf(str_time_login, sizeof(str_time_login),
88 "%d年%d天", p_tm->tm_year - 70, p_tm->tm_yday);
89 }
90 else if (p_tm->tm_yday > 0)
91 {
92 snprintf(str_time_login, sizeof(str_time_login),
93 "%d天", p_tm->tm_yday);
94 }
95 else if (p_tm->tm_hour > 0)
96 {
97 snprintf(str_time_login, sizeof(str_time_login),
98 "%d:%.2d", p_tm->tm_hour, p_tm->tm_min);
99 }
100 else
101 {
102 snprintf(str_time_login, sizeof(str_time_login),
103 "%d\'%.2d\"", p_tm->tm_min, p_tm->tm_sec);
104 }
105
106 moveto(4 + i, 1);
107
108 prints(" %6d %s%*s %s%*s %s",
109 p_users[i].id + 1,
110 p_users[i].username,
111 BBS_username_max_len - str_length(p_users[i].username, 1),
112 "",
113 p_users[i].nickname,
114 BBS_nickname_max_len / 2 - str_length(p_users[i].nickname, 1),
115 "",
116 str_time_login);
117 }
118
119 return 0;
120 }
121
122 static int user_online_list_draw_items(int page_id, USER_ONLINE_INFO *p_users, int user_count)
123 {
124 char str_time_login[LINE_BUFFER_LEN];
125 char str_time_idle[LINE_BUFFER_LEN];
126 const char *p_action_title;
127 time_t tm_now;
128 time_t tm_duration;
129 struct tm *p_tm;
130 int i;
131
132 clrline(4, 23);
133
134 time(&tm_now);
135
136 for (i = 0; i < user_count; i++)
137 {
138 tm_duration = tm_now - p_users[i].login_tm;
139 p_tm = gmtime(&tm_duration);
140 if (p_tm == NULL)
141 {
142 log_error("Invalid time duration\n");
143 str_time_login[0] = '\0';
144 }
145 else if (p_tm->tm_yday > 0)
146 {
147 snprintf(str_time_login, sizeof(str_time_login),
148 "%dd%dh", p_tm->tm_yday, p_tm->tm_hour);
149 }
150 else if (p_tm->tm_hour > 0)
151 {
152 snprintf(str_time_login, sizeof(str_time_login),
153 "%d:%.2d", p_tm->tm_hour, p_tm->tm_min);
154 }
155 else
156 {
157 snprintf(str_time_login, sizeof(str_time_login),
158 "%d\'%.2d\"", p_tm->tm_min, p_tm->tm_sec);
159 }
160
161 tm_duration = tm_now - p_users[i].last_tm;
162 p_tm = gmtime(&tm_duration);
163 if (p_tm == NULL)
164 {
165 log_error("Invalid time duration\n");
166 str_time_idle[0] = '\0';
167 }
168 else if (p_tm->tm_min > 0)
169 {
170 snprintf(str_time_idle, sizeof(str_time_idle),
171 "%d\'%d\"", p_tm->tm_min, p_tm->tm_sec);
172 }
173 else
174 {
175 snprintf(str_time_idle, sizeof(str_time_idle),
176 "%d\"", p_tm->tm_sec);
177 }
178
179 p_action_title = (p_users[i].current_action_title != NULL
180 ? p_users[i].current_action_title
181 : p_users[i].current_action);
182
183 moveto(4 + i, 1);
184
185 prints(" %6d %s%*s %s%*s %s%*s %s%*s %s",
186 p_users[i].id + 1,
187 p_users[i].user_info.username,
188 BBS_username_max_len - str_length(p_users[i].user_info.username, 1),
189 "",
190 p_users[i].user_info.nickname,
191 BBS_nickname_max_len / 2 - str_length(p_users[i].user_info.nickname, 1),
192 "",
193 str_time_login,
194 8 - str_length(str_time_login, 1),
195 "",
196 str_time_idle,
197 6 - str_length(str_time_idle, 1),
198 "",
199 p_action_title);
200 }
201
202 return 0;
203 }
204
205 static enum select_cmd_t user_list_select(int total_page, int item_count, int *p_page_id, int *p_selected_index)
206 {
207 int old_page_id = *p_page_id;
208 int old_selected_index = *p_selected_index;
209 int ch;
210
211 if (item_count > 0 && *p_selected_index >= 0)
212 {
213 moveto(4 + *p_selected_index, 1);
214 outc('>');
215 iflush();
216 }
217
218 while (!SYS_server_exit)
219 {
220 ch = igetch(100);
221
222 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
223 {
224 BBS_last_access_tm = time(NULL);
225 }
226
227 switch (ch)
228 {
229 case KEY_NULL: // broken pipe
230 log_error("KEY_NULL\n");
231 case KEY_ESC:
232 case KEY_LEFT:
233 return EXIT_LIST; // exit list
234 case KEY_TIMEOUT:
235 if (time(NULL) - BBS_last_access_tm >= MAX_DELAY_TIME)
236 {
237 log_error("User input timeout\n");
238 return EXIT_LIST; // exit list
239 }
240 continue;
241 case CR:
242 case KEY_RIGHT:
243 if (item_count > 0)
244 {
245 return VIEW_USER;
246 }
247 break;
248 case KEY_HOME:
249 *p_page_id = 0;
250 case 'P':
251 case KEY_PGUP:
252 *p_selected_index = 0;
253 case 'k':
254 case KEY_UP:
255 if (*p_selected_index <= 0)
256 {
257 if (*p_page_id > 0)
258 {
259 (*p_page_id)--;
260 *p_selected_index = BBS_user_limit_per_page - 1;
261 }
262 else if (ch == KEY_UP || ch == 'k') // Rotate to the tail of list
263 {
264 if (total_page > 0)
265 {
266 *p_page_id = total_page - 1;
267 }
268 if (item_count > 0)
269 {
270 *p_selected_index = item_count - 1;
271 }
272 }
273 }
274 else
275 {
276 (*p_selected_index)--;
277 }
278 break;
279 case '$':
280 case KEY_END:
281 if (total_page > 0)
282 {
283 *p_page_id = total_page - 1;
284 }
285 case 'N':
286 case KEY_PGDN:
287 if (item_count > 0)
288 {
289 *p_selected_index = item_count - 1;
290 }
291 case 'j':
292 case KEY_DOWN:
293 if (*p_selected_index + 1 >= item_count) // next page
294 {
295 if (*p_page_id + 1 < total_page)
296 {
297 (*p_page_id)++;
298 *p_selected_index = 0;
299 }
300 else if (ch == KEY_DOWN || ch == 'j') // Rotate to the head of list
301 {
302 *p_page_id = 0;
303 *p_selected_index = 0;
304 }
305 }
306 else
307 {
308 (*p_selected_index)++;
309 }
310 break;
311 case 's':
312 return SEARCH_USER;
313 case KEY_F5:
314 return REFRESH_LIST;
315 case 'h':
316 return SHOW_HELP;
317 default:
318 break;
319 }
320
321 if (old_page_id != *p_page_id)
322 {
323 return CHANGE_PAGE;
324 }
325
326 if (item_count > 0 && old_selected_index != *p_selected_index)
327 {
328 if (old_selected_index >= 0)
329 {
330 moveto(4 + old_selected_index, 1);
331 outc(' ');
332 }
333 if (*p_selected_index >= 0)
334 {
335 moveto(4 + *p_selected_index, 1);
336 outc('>');
337 }
338 iflush();
339
340 old_selected_index = *p_selected_index;
341 }
342 }
343
344 return EXIT_LIST;
345 }
346
347 int user_list_display(int online_user)
348 {
349 char page_info_str[LINE_BUFFER_LEN];
350 USER_INFO users[BBS_user_limit_per_page];
351 USER_ONLINE_INFO online_users[BBS_user_limit_per_page];
352 int user_count = 0;
353 int page_count = 0;
354 int page_id = 0;
355 int selected_index = 0;
356 int ret = 0;
357
358 user_list_draw_screen(online_user);
359
360 if (online_user)
361 {
362 ret = query_user_online_list(page_id, online_users, &user_count, &page_count);
363 if (ret < 0)
364 {
365 log_error("query_user_online_list(page_id=%d) error\n", page_id);
366 return -2;
367 }
368 }
369 else
370 {
371 ret = query_user_list(page_id, users, &user_count, &page_count);
372 if (ret < 0)
373 {
374 log_error("query_user_list(page_id=%d) error\n", page_id);
375 return -2;
376 }
377 }
378
379 if (user_count == 0) // empty list
380 {
381 selected_index = 0;
382 }
383
384 while (!SYS_server_exit)
385 {
386 if (online_user)
387 {
388 ret = user_online_list_draw_items(page_id, online_users, user_count);
389 if (ret < 0)
390 {
391 log_error("user_online_list_draw_items(page_id=%d) error\n", page_id);
392 return -3;
393 }
394 }
395 else
396 {
397 ret = user_list_draw_items(page_id, users, user_count);
398 if (ret < 0)
399 {
400 log_error("user_list_draw_items(page_id=%d) error\n", page_id);
401 return -3;
402 }
403 }
404
405 snprintf(page_info_str, sizeof(page_info_str),
406 "\033[33m[第\033[36m%d\033[33m/\033[36m%d\033[33m页]",
407 page_id + 1, MAX(page_count, 1));
408
409 show_bottom(page_info_str);
410 iflush();
411
412 if (user_online_update(online_user ? "USER_ONLINE" : "USER_LIST") < 0)
413 {
414 log_error("user_online_update(%s) error\n",
415 (online_user ? "USER_ONLINE" : "USER_LIST"));
416 }
417
418 ret = user_list_select(page_count, user_count, &page_id, &selected_index);
419 switch (ret)
420 {
421 case EXIT_LIST:
422 return 0;
423 case REFRESH_LIST:
424 case CHANGE_PAGE:
425 if (online_user)
426 {
427 ret = query_user_online_list(page_id, online_users, &user_count, &page_count);
428 if (ret < 0)
429 {
430 log_error("query_user_online_list(page_id=%d) error\n", page_id);
431 return -2;
432 }
433 }
434 else
435 {
436 ret = query_user_list(page_id, users, &user_count, &page_count);
437 if (ret < 0)
438 {
439 log_error("query_user_list(page_id=%d) error\n", page_id);
440 return -2;
441 }
442 }
443
444 if (user_count == 0) // empty list
445 {
446 selected_index = 0;
447 }
448 else if (selected_index >= user_count)
449 {
450 selected_index = user_count - 1;
451 }
452 break;
453 case VIEW_USER:
454 user_info_display(online_user ? &(online_users[selected_index].user_info) : &(users[selected_index]));
455 user_list_draw_screen(online_user);
456 break;
457 case SEARCH_USER:
458 user_list_search();
459 user_list_draw_screen(online_user);
460 break;
461 case SHOW_HELP:
462 // Display help information
463 display_file(DATA_READ_HELP, 1);
464 user_list_draw_screen(online_user);
465 break;
466 default:
467 log_error("Unknown command %d\n", ret);
468 }
469 }
470
471 return 0;
472 }
473
474 int user_list_search(void)
475 {
476 const int users_per_line = 5;
477 const int max_user_lines = 20;
478 const int max_user_cnt = users_per_line * max_user_lines + 1;
479
480 char username[BBS_username_max_len + 1];
481 int32_t uid_list[max_user_cnt];
482 char username_list[max_user_cnt][BBS_username_max_len + 1];
483 int ret;
484 int i;
485 USER_INFO user_info;
486 char user_intro[BBS_user_intro_max_len];
487 int ok;
488
489 username[0] = '\0';
490
491 clearscr();
492
493 while (!SYS_server_exit)
494 {
495 get_data(2, 1, "查找谁: ", username, sizeof(username), BBS_username_max_len);
496
497 if (username[0] == '\0')
498 {
499 return 0;
500 }
501
502 // Verify format
503 for (i = 0, ok = 1; ok && username[i] != '\0'; i++)
504 {
505 if (!(isalpha(username[i]) || (i > 0 && (isdigit(username[i]) || username[i] == '_'))))
506 {
507 ok = 0;
508 }
509 }
510 if (ok && i > BBS_username_max_len)
511 {
512 ok = 0;
513 }
514 if (!ok)
515 {
516 moveto(3, 1);
517 clrtoeol();
518 prints("用户名格式非法");
519 continue;
520 }
521
522 clrline(3, SCREEN_ROWS);
523
524 ret = query_user_info_by_username(username, max_user_cnt, uid_list, username_list);
525
526 if (ret < 0)
527 {
528 log_error("query_user_info_by_username(%s) error\n", username);
529 return -1;
530 }
531 else if (ret > 1)
532 {
533 moveto(3, 1);
534 prints("存在多个匹配的用户,[S]精确查找,[L]列出全部? [L]");
535 iflush();
536 igetch_reset();
537
538 switch (igetch_t(MAX_DELAY_TIME))
539 {
540 case KEY_NULL:
541 case KEY_TIMEOUT:
542 return -1;
543 case KEY_ESC:
544 return 0;
545 case 'S':
546 case 's':
547 ret = (strcasecmp(username_list[0], username) == 0 ? 1 : 0);
548 break;
549 case 'L':
550 case 'l':
551 case CR:
552 for (i = 0; i < MIN(ret, users_per_line * max_user_lines); i++)
553 {
554 moveto(4 + i / users_per_line, 3 + i % users_per_line * (BBS_username_max_len + 3));
555 prints("%s", username_list[i]);
556 }
557 moveto(SCREEN_ROWS, 1);
558 if (ret > users_per_line * max_user_lines)
559 {
560 prints("还有更多...");
561 }
562 continue;
563 default:
564 moveto(3, 1);
565 clrtoeol();
566 continue;
567 }
568 }
569
570 if (ret == 0)
571 {
572 moveto(3, 1);
573 clrtoeol();
574 prints("没有找到符合条件的用户");
575 press_any_key();
576 return 0;
577 }
578 else // ret == 1
579 {
580 if (query_user_info_by_uid(uid_list[0], &user_info, user_intro, sizeof(user_intro)) <= 0)
581 {
582 log_error("query_user_info_by_uid(uid=%d) error\n", uid_list[0]);
583 return -2;
584 }
585 else if (user_info_display(&user_info) < 0)
586 {
587 log_error("user_info_display(uid=%d) error\n", uid_list[0]);
588 return -3;
589 }
590 return 1;
591 }
592 }
593
594 return 0;
595 }

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