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

Contents of /lbbs/src/bbs_net.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.81 - (show annotations)
Tue Nov 11 00:28:05 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.80: +4 -0 lines
Content type: text/x-csrc
Use config.h

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * bbs_net
4 * - user interactive feature of site shuttle
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "bbs.h"
14 #include "common.h"
15 #include "io.h"
16 #include "log.h"
17 #include "login.h"
18 #include "menu.h"
19 #include "screen.h"
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <netdb.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <arpa/inet.h>
30 #include <libssh/libssh.h>
31 #include <libssh/server.h>
32 #include <libssh/callbacks.h>
33 #include <netinet/in.h>
34 #include <netinet/ip.h>
35 #include <sys/select.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38 #include <sys/epoll.h>
39
40 static const char MENU_CONF_DELIM[] = " \t\r\n";
41
42 enum _bbs_net_constant_t
43 {
44 MAX_PROCESS_BAR_LEN = 30,
45 MAXSTATION = 26 * 2,
46 STATION_PER_LINE = 4,
47 };
48
49 struct _bbsnet_conf
50 {
51 char host1[20];
52 char host2[40];
53 char ip[40];
54 in_port_t port;
55 char charset[CHARSET_MAX_LEN + 1];
56 } bbsnet_conf[MAXSTATION];
57
58 static MENU_SET bbsnet_menu;
59
60 static int load_bbsnet_conf(const char *file_config)
61 {
62 FILE *fp;
63 MENU *p_menu;
64 MENU_ITEM *p_menu_item;
65 MENU_ITEM_ID menu_item_id;
66 char t[256], *t1, *t2, *t3, *t4, *t5, *saveptr;
67
68 fp = fopen(file_config, "r");
69 if (fp == NULL)
70 {
71 return -1;
72 }
73
74 bbsnet_menu.p_menu_pool = calloc(1, sizeof(MENU));
75 if (bbsnet_menu.p_menu_pool == NULL)
76 {
77 log_error("calloc(p_menu_pool) error\n");
78 return -3;
79 }
80 bbsnet_menu.menu_count = 1;
81
82 bbsnet_menu.p_menu_item_pool = calloc(MAXSTATION, sizeof(MENU_ITEM));
83 if (bbsnet_menu.p_menu_item_pool == NULL)
84 {
85 log_error("calloc(p_menu_item_pool) error\n");
86 return -3;
87 }
88 bbsnet_menu.menu_item_count = MAXSTATION;
89
90 p_menu = (MENU *)get_menu_by_id(&bbsnet_menu, 0);
91
92 strncpy(p_menu->name, "BBSNET", sizeof(p_menu->name) - 1);
93 p_menu->name[sizeof(p_menu->name) - 1] = '\0';
94 p_menu->title.show = 0;
95 p_menu->screen_show = 0;
96
97 menu_item_id = 0;
98 while (fgets(t, 255, fp) && menu_item_id < MAXSTATION)
99 {
100 t1 = strtok_r(t, MENU_CONF_DELIM, &saveptr);
101 t2 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
102 t3 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
103 t4 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
104 t5 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr);
105
106 if (t1 == NULL || t2 == NULL || t3 == NULL || t4 == NULL || t5 == NULL || t[0] == '#' || t[0] == '*')
107 {
108 continue;
109 }
110
111 strncpy(bbsnet_conf[menu_item_id].host1, t2, sizeof(bbsnet_conf[menu_item_id].host1) - 1);
112 bbsnet_conf[menu_item_id].host1[sizeof(bbsnet_conf[menu_item_id].host1) - 1] = '\0';
113 strncpy(bbsnet_conf[menu_item_id].host2, t1, sizeof(bbsnet_conf[menu_item_id].host2) - 1);
114 bbsnet_conf[menu_item_id].host2[sizeof(bbsnet_conf[menu_item_id].host2) - 1] = '\0';
115 strncpy(bbsnet_conf[menu_item_id].ip, t3, sizeof(bbsnet_conf[menu_item_id].ip) - 1);
116 bbsnet_conf[menu_item_id].ip[sizeof(bbsnet_conf[menu_item_id].ip) - 1] = '\0';
117 bbsnet_conf[menu_item_id].port = (in_port_t)(t4 ? atoi(t4) : 23);
118 strncpy(bbsnet_conf[menu_item_id].charset, t5, sizeof(bbsnet_conf[menu_item_id].charset) - 1);
119 bbsnet_conf[menu_item_id].charset[sizeof(bbsnet_conf[menu_item_id].charset) - 1] = '\0';
120
121 p_menu_item = get_menu_item_by_id(&bbsnet_menu, menu_item_id);
122 if (p_menu_item == NULL)
123 {
124 log_error("get_menu_item_by_id(%d) return NULL pointer\n", menu_item_id);
125 return -1;
126 }
127
128 p_menu_item->row = (int16_t)(2 + menu_item_id / STATION_PER_LINE);
129 p_menu_item->col = (int16_t)(5 + menu_item_id % STATION_PER_LINE * 20);
130 snprintf(p_menu_item->action, sizeof(p_menu_item->action), "%d", (int16_t)menu_item_id);
131 p_menu_item->submenu = 0;
132 p_menu_item->priv = 0;
133 p_menu_item->level = 0;
134 p_menu_item->name[0] =
135 (char)(menu_item_id < MAXSTATION / 2 ? 'A' + menu_item_id : 'a' + menu_item_id);
136 p_menu_item->name[1] = '\0';
137 snprintf(p_menu_item->text, sizeof(p_menu_item->text), "%c. %s",
138 p_menu_item->name[0], bbsnet_conf[menu_item_id].host1);
139
140 p_menu->items[p_menu->item_count] = menu_item_id;
141 p_menu->item_count++;
142 menu_item_id++;
143 }
144
145 bbsnet_menu.menu_item_count = (int16_t)menu_item_id;
146 bbsnet_menu.menu_id_path[0] = 0;
147 bbsnet_menu.menu_item_pos[0] = 0;
148 bbsnet_menu.choose_step = 0;
149
150 fclose(fp);
151
152 return 0;
153 }
154
155 static void unload_bbsnet_conf(void)
156 {
157 bbsnet_menu.menu_count = 0;
158 bbsnet_menu.menu_item_count = 0;
159
160 free(bbsnet_menu.p_menu_pool);
161 bbsnet_menu.p_menu_pool = NULL;
162 free(bbsnet_menu.p_menu_item_pool);
163 bbsnet_menu.p_menu_item_pool = NULL;
164 }
165
166 static void process_bar(int n, int len)
167 {
168 char buf[LINE_BUFFER_LEN];
169 char buf2[LINE_BUFFER_LEN];
170
171 if (len > LINE_BUFFER_LEN)
172 {
173 len = LINE_BUFFER_LEN - 1;
174 }
175 if (n < 0)
176 {
177 n = 0;
178 }
179 else if (n > len)
180 {
181 n = len;
182 }
183
184 moveto(4, 0);
185 prints(" ------------------------------ \r\n");
186 snprintf(buf, sizeof(buf), " %3d%% ", n * 100 / len);
187 memcpy(buf2, buf, (size_t)n);
188 buf2[n] = '\0';
189 prints("|\033[46m%s\033[44m%s\033[m|\r\n", buf2, buf + n);
190 prints(" ------------------------------ \r\n");
191 iflush();
192 }
193
194 static int bbsnet_connect(int n)
195 {
196 int sock, ret, loop, error;
197 int sock_connected = 0;
198 int flags_sock;
199 int flags_stdin;
200 int flags_stdout;
201 int len;
202 struct sockaddr_in sin;
203 char input_buf[LINE_BUFFER_LEN];
204 char output_buf[LINE_BUFFER_LEN];
205 int input_buf_len = 0;
206 int output_buf_len = 0;
207 int input_buf_offset = 0;
208 int output_buf_offset = 0;
209 char input_conv[LINE_BUFFER_LEN * 2];
210 char output_conv[LINE_BUFFER_LEN * 2];
211 int input_conv_len = 0;
212 int output_conv_len = 0;
213 int input_conv_offset = 0;
214 int output_conv_offset = 0;
215 iconv_t input_cd = NULL;
216 iconv_t output_cd = NULL;
217 char tocode[32];
218 struct epoll_event ev, events[MAX_EVENTS];
219 int nfds, epollfd;
220 int stdin_read_wait = 0;
221 int stdout_write_wait = 0;
222 int sock_read_wait = 0;
223 int sock_write_wait = 0;
224 struct hostent *p_host = NULL;
225 int tos;
226 char remote_addr[IP_ADDR_LEN];
227 int remote_port;
228 char local_addr[IP_ADDR_LEN];
229 int local_port;
230 socklen_t sock_len;
231 time_t t_used = time(NULL);
232 struct tm *tm_used;
233 int ch;
234
235 if (user_online_update("BBS_NET") < 0)
236 {
237 log_error("user_online_update(BBS_NET) error\n");
238 }
239
240 clearscr();
241
242 moveto(0, 0);
243 prints("\033[1;32m正在测试往 %s (%s) 的连接,请稍候... \033[m\r\n",
244 bbsnet_conf[n].host1, bbsnet_conf[n].ip);
245 iflush();
246
247 p_host = gethostbyname(bbsnet_conf[n].ip);
248
249 if (p_host == NULL)
250 {
251 prints("\033[1;31m查找主机名失败!\033[m\r\n");
252 press_any_key();
253 return -1;
254 }
255
256 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
257
258 if (sock < 0)
259 {
260 prints("\033[1;31m无法创建socket!\033[m\r\n");
261 press_any_key();
262 return -1;
263 }
264
265 sin.sin_family = AF_INET;
266 sin.sin_addr.s_addr = (BBS_address[0] != '\0' ? inet_addr(BBS_address) : INADDR_ANY);
267 sin.sin_port = 0;
268
269 if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
270 {
271 log_error("Bind address %s:%u failed (%d)\n",
272 inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), errno);
273 return -2;
274 }
275
276 memset(&sin, 0, sizeof(sin));
277 sin.sin_family = AF_INET;
278 sin.sin_addr = *(struct in_addr *)p_host->h_addr_list[0];
279 sin.sin_port = htons(bbsnet_conf[n].port);
280
281 strncpy(remote_addr, inet_ntoa(sin.sin_addr), sizeof(remote_addr) - 1);
282 remote_addr[sizeof(remote_addr) - 1] = '\0';
283 remote_port = ntohs(sin.sin_port);
284
285 prints("\033[1;32m穿梭进度条提示您当前已使用的时间,按\033[1;33mCtrl+C\033[1;32m中断。\033[m\r\n");
286 process_bar(0, MAX_PROCESS_BAR_LEN);
287
288 // Set socket as non-blocking
289 flags_sock = fcntl(sock, F_GETFL, 0);
290 fcntl(sock, F_SETFL, flags_sock | O_NONBLOCK);
291
292 // Set STDIN/STDOUT as non-blocking
293 flags_stdin = fcntl(STDIN_FILENO, F_GETFL, 0);
294 flags_stdout = fcntl(STDOUT_FILENO, F_GETFL, 0);
295 fcntl(STDIN_FILENO, F_SETFL, flags_stdin | O_NONBLOCK);
296 fcntl(STDOUT_FILENO, F_SETFL, flags_stdout | O_NONBLOCK);
297
298 epollfd = epoll_create1(0);
299 if (epollfd < 0)
300 {
301 log_error("epoll_create1() error (%d)\n", errno);
302 return -1;
303 }
304
305 ev.events = EPOLLOUT | EPOLLET;
306 ev.data.fd = sock;
307 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev) == -1)
308 {
309 log_error("epoll_ctl(socket) error (%d)\n", errno);
310 goto cleanup;
311 }
312
313 ev.events = EPOLLIN | EPOLLET;
314 ev.data.fd = STDIN_FILENO;
315 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
316 {
317 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
318 goto cleanup;
319 }
320
321 while (!SYS_server_exit)
322 {
323 if ((ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin))) < 0)
324 {
325 if (errno == EAGAIN || errno == EALREADY || errno == EINPROGRESS)
326 {
327 // Use select / epoll to check writability of the socket,
328 // then use getsockopt to check the status of the socket.
329 // See man connect(2)
330 break;
331 }
332 else if (errno == EINTR)
333 {
334 continue;
335 }
336 else
337 {
338 log_error("connect(socket) error (%d)\n", errno);
339
340 prints("\033[1;31m连接失败!\033[m\r\n");
341 press_any_key();
342
343 goto cleanup;
344 }
345 }
346 }
347
348 for (int j = 0; j < MAX_PROCESS_BAR_LEN && !sock_connected && !SYS_server_exit; j++)
349 {
350 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 500); // 0.5 second
351
352 if (nfds < 0)
353 {
354 if (errno != EINTR)
355 {
356 log_error("epoll_wait() error (%d)\n", errno);
357 break;
358 }
359 }
360 else if (nfds == 0) // timeout
361 {
362 process_bar(j + 1, MAX_PROCESS_BAR_LEN);
363 }
364 else // ret > 0
365 {
366 for (int i = 0; i < nfds; i++)
367 {
368 if (events[i].data.fd == sock)
369 {
370 len = sizeof(error);
371 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0)
372 {
373 log_error("getsockopt() error (%d) !\n", error);
374 goto cleanup;
375 }
376 if (error == 0)
377 {
378 sock_connected = 1;
379 }
380 }
381 else if (events[i].data.fd == STDIN_FILENO)
382 {
383 ch = igetch(0);
384 if (ch == Ctrl('C') || ch == KEY_ESC)
385 {
386 goto cleanup;
387 }
388 }
389 }
390 }
391 }
392 if (SYS_server_exit)
393 {
394 goto cleanup;
395 }
396 if (!sock_connected)
397 {
398 prints("\033[1;31m连接失败!\033[m\r\n");
399 press_any_key();
400
401 goto cleanup;
402 }
403
404 tos = IPTOS_LOWDELAY;
405 if (setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
406 {
407 log_error("setsockopt IP_TOS=%d error (%d)\n", tos, errno);
408 }
409
410 sock_len = sizeof(sin);
411 if (getsockname(sock, (struct sockaddr *)&sin, &sock_len) < 0)
412 {
413 log_error("getsockname() error: %d", errno);
414 goto cleanup;
415 }
416
417 strncpy(local_addr, inet_ntoa(sin.sin_addr), sizeof(local_addr) - 1);
418 local_addr[sizeof(local_addr) - 1] = '\0';
419 local_port = ntohs(sin.sin_port);
420
421 prints("\033[1;31m连接成功!\033[m\r\n");
422 iflush();
423 log_common("BBSNET connect to %s:%d from %s:%d by [%s]\n",
424 remote_addr, remote_port, local_addr, local_port, BBS_username);
425
426 snprintf(tocode, sizeof(tocode), "%s%s", bbsnet_conf[n].charset,
427 (strcasecmp(stdio_charset, bbsnet_conf[n].charset) == 0 ? "" : "//IGNORE"));
428 input_cd = iconv_open(tocode, stdio_charset);
429 if (input_cd == (iconv_t)(-1))
430 {
431 log_error("iconv_open(%s->%s) error: %d\n", stdio_charset, tocode, errno);
432 goto cleanup;
433 }
434
435 snprintf(tocode, sizeof(tocode), "%s%s", stdio_charset,
436 (strcasecmp(bbsnet_conf[n].charset, stdio_charset) == 0 ? "" : "//TRANSLIT"));
437 output_cd = iconv_open(tocode, bbsnet_conf[n].charset);
438 if (output_cd == (iconv_t)(-1))
439 {
440 log_error("iconv_open(%s->%s) error: %d\n", bbsnet_conf[n].charset, tocode, errno);
441 iconv_close(input_cd);
442 goto cleanup;
443 }
444
445 ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
446 ev.data.fd = sock;
447 if (epoll_ctl(epollfd, EPOLL_CTL_MOD, sock, &ev) == -1)
448 {
449 log_error("epoll_ctl(socket) error (%d)\n", errno);
450 goto cleanup;
451 }
452
453 ev.events = EPOLLOUT | EPOLLET;
454 ev.data.fd = STDOUT_FILENO;
455 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
456 {
457 log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
458 goto cleanup;
459 }
460
461 BBS_last_access_tm = t_used = time(NULL);
462 loop = 1;
463
464 while (loop && !SYS_server_exit)
465 {
466 if (SSH_v2 && ssh_channel_is_closed(SSH_channel))
467 {
468 log_error("SSH channel is closed\n");
469 loop = 0;
470 break;
471 }
472
473 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
474
475 if (nfds < 0)
476 {
477 if (errno != EINTR)
478 {
479 log_error("epoll_wait() error (%d)\n", errno);
480 break;
481 }
482 continue;
483 }
484 else if (nfds == 0) // timeout
485 {
486 if (time(NULL) - BBS_last_access_tm >= BBS_max_user_idle_time)
487 {
488 break;
489 }
490 }
491
492 for (int i = 0; i < nfds; i++)
493 {
494 if (events[i].data.fd == STDIN_FILENO)
495 {
496 stdin_read_wait = 1;
497 }
498
499 if (events[i].data.fd == sock)
500 {
501 if (events[i].events & EPOLLIN)
502 {
503 sock_read_wait = 1;
504 }
505 if (events[i].events & EPOLLOUT)
506 {
507 sock_write_wait = 1;
508 }
509 }
510
511 if (events[i].data.fd == STDOUT_FILENO)
512 {
513 stdout_write_wait = 1;
514 }
515 }
516
517 if (stdin_read_wait)
518 {
519 while (input_buf_len < sizeof(input_buf) && !SYS_server_exit)
520 {
521 if (SSH_v2)
522 {
523 ret = ssh_channel_read_nonblocking(SSH_channel, input_buf + input_buf_len, sizeof(input_buf) - (uint32_t)input_buf_len, 0);
524 if (ret == SSH_ERROR)
525 {
526 log_error("ssh_channel_read_nonblocking() error: %s\n", ssh_get_error(SSH_session));
527 loop = 0;
528 break;
529 }
530 else if (ret == SSH_EOF)
531 {
532 stdin_read_wait = 0;
533 loop = 0;
534 break;
535 }
536 else if (ret == 0)
537 {
538 // Send NO-OP to remote server
539 input_buf[input_buf_len] = '\0';
540 input_buf_len++;
541 BBS_last_access_tm = time(NULL);
542
543 stdin_read_wait = 0;
544 break; // Check whether channel is still open
545 }
546 }
547 else
548 {
549 ret = (int)read(STDIN_FILENO, input_buf + input_buf_len, sizeof(input_buf) - (size_t)input_buf_len);
550 }
551 if (ret < 0)
552 {
553 if (errno == EAGAIN || errno == EWOULDBLOCK)
554 {
555 stdin_read_wait = 0;
556 break;
557 }
558 else if (errno == EINTR)
559 {
560 continue;
561 }
562 else
563 {
564 log_error("read(STDIN) error (%d)\n", errno);
565 loop = 0;
566 break;
567 }
568 }
569 else if (ret == 0) // broken pipe
570 {
571 #ifdef _DEBUG
572 log_error("read(STDIN) EOF\n");
573 #endif
574 stdin_read_wait = 0;
575 loop = 0;
576 break;
577 }
578 else
579 {
580 input_buf_len += ret;
581 BBS_last_access_tm = time(NULL);
582
583 // Refresh current action while user input
584 if (user_online_update("BBS_NET") < 0)
585 {
586 log_error("user_online_update(BBS_NET) error\n");
587 }
588
589 continue;
590 }
591 }
592 }
593
594 if (sock_write_wait)
595 {
596 if (input_buf_offset < input_buf_len)
597 {
598 // For debug
599 #ifdef _DEBUG
600 for (int j = input_buf_offset; j < input_buf_len; j++)
601 {
602 log_error("Debug input: <--[%u]\n", (input_buf[j] + 256) % 256);
603 }
604 #endif
605
606 ret = io_buf_conv(input_cd, input_buf, &input_buf_len, &input_buf_offset, input_conv, sizeof(input_conv), &input_conv_len);
607 if (ret < 0)
608 {
609 log_error("io_buf_conv(input, %d, %d, %d) error\n", input_buf_len, input_buf_offset, input_conv_len);
610 input_buf_len = input_buf_offset; // Discard invalid sequence
611 }
612
613 // For debug
614 #ifdef _DEBUG
615 for (int j = input_conv_offset; j < input_conv_len; j++)
616 {
617 log_error("Debug input_conv: <--[%u]\n", (input_conv[j] + 256) % 256);
618 }
619 #endif
620 }
621
622 while (input_conv_offset < input_conv_len && !SYS_server_exit)
623 {
624 ret = (int)write(sock, input_conv + input_conv_offset, (size_t)(input_conv_len - input_conv_offset));
625 if (ret < 0)
626 {
627 if (errno == EAGAIN || errno == EWOULDBLOCK)
628 {
629 sock_write_wait = 0;
630 break;
631 }
632 else if (errno == EINTR)
633 {
634 continue;
635 }
636 else
637 {
638 log_error("write(socket) error (%d)\n", errno);
639 loop = 0;
640 break;
641 }
642 }
643 else if (ret == 0) // broken pipe
644 {
645 #ifdef _DEBUG
646 log_error("write(socket) EOF\n");
647 #endif
648 sock_write_wait = 0;
649 loop = 0;
650 break;
651 }
652 else
653 {
654 input_conv_offset += ret;
655 if (input_conv_offset >= input_conv_len) // Output buffer complete
656 {
657 input_conv_offset = 0;
658 input_conv_len = 0;
659 break;
660 }
661 continue;
662 }
663 }
664 }
665
666 if (sock_read_wait)
667 {
668 while (output_buf_len < sizeof(output_buf) && !SYS_server_exit)
669 {
670 ret = (int)read(sock, output_buf + output_buf_len, sizeof(output_buf) - (size_t)output_buf_len);
671 if (ret < 0)
672 {
673 if (errno == EAGAIN || errno == EWOULDBLOCK)
674 {
675 sock_read_wait = 0;
676 break;
677 }
678 else if (errno == EINTR)
679 {
680 continue;
681 }
682 else
683 {
684 log_error("read(socket) error (%d)\n", errno);
685 loop = 0;
686 break;
687 }
688 }
689 else if (ret == 0) // broken pipe
690 {
691 #ifdef _DEBUG
692 log_error("read(socket) EOF\n");
693 #endif
694 sock_read_wait = 0;
695 loop = 0;
696 break;
697 }
698 else
699 {
700 output_buf_len += ret;
701 continue;
702 }
703 }
704 }
705
706 if (stdout_write_wait)
707 {
708 if (output_buf_offset < output_buf_len)
709 {
710 ret = io_buf_conv(output_cd, output_buf, &output_buf_len, &output_buf_offset, output_conv, sizeof(output_conv), &output_conv_len);
711 if (ret < 0)
712 {
713 log_error("io_buf_conv(output, %d, %d, %d) error\n", output_buf_len, output_buf_offset, output_conv_len);
714 output_buf_len = output_buf_offset; // Discard invalid sequence
715 }
716 }
717
718 while (output_conv_offset < output_conv_len && !SYS_server_exit)
719 {
720 if (SSH_v2)
721 {
722 ret = ssh_channel_write(SSH_channel, output_conv + output_conv_offset, (uint32_t)(output_conv_len - output_conv_offset));
723 if (ret == SSH_ERROR)
724 {
725 log_error("ssh_channel_write() error: %s\n", ssh_get_error(SSH_session));
726 loop = 0;
727 break;
728 }
729 }
730 else
731 {
732 ret = (int)write(STDOUT_FILENO, output_conv + output_conv_offset, (size_t)(output_conv_len - output_conv_offset));
733 }
734 if (ret < 0)
735 {
736 if (errno == EAGAIN || errno == EWOULDBLOCK)
737 {
738 stdout_write_wait = 0;
739 break;
740 }
741 else if (errno == EINTR)
742 {
743 continue;
744 }
745 else
746 {
747 log_error("write(STDOUT) error (%d)\n", errno);
748 loop = 0;
749 break;
750 }
751 }
752 else if (ret == 0) // broken pipe
753 {
754 #ifdef _DEBUG
755 log_error("write(STDOUT) EOF\n");
756 #endif
757 stdout_write_wait = 0;
758 loop = 0;
759 break;
760 }
761 else
762 {
763 output_conv_offset += ret;
764 if (output_conv_offset >= output_conv_len) // Output buffer complete
765 {
766 output_conv_offset = 0;
767 output_conv_len = 0;
768 break;
769 }
770 continue;
771 }
772 }
773 }
774 }
775
776 iconv_close(input_cd);
777 iconv_close(output_cd);
778
779 cleanup:
780 if (close(epollfd) < 0)
781 {
782 log_error("close(epoll) error (%d)\n");
783 }
784
785 // Restore STDIN/STDOUT flags
786 fcntl(STDIN_FILENO, F_SETFL, flags_stdin);
787 fcntl(STDOUT_FILENO, F_SETFL, flags_stdout);
788
789 // Restore socket flags
790 fcntl(sock, F_SETFL, flags_sock);
791
792 if (close(sock) == -1)
793 {
794 log_error("Close socket failed\n");
795 }
796
797 t_used = time(NULL) - t_used;
798 tm_used = gmtime(&t_used);
799
800 log_common("BBSNET disconnect, %d days %d hours %d minutes %d seconds used\n",
801 tm_used->tm_mday - 1, tm_used->tm_hour, tm_used->tm_min,
802 tm_used->tm_sec);
803
804 BBS_last_access_tm = time(NULL);
805
806 return 0;
807 }
808
809 static int bbsnet_refresh()
810 {
811 clearscr();
812 moveto(1, 0);
813 prints(" ----------------------------------------------------------------------------- ");
814 for (int i = 2; i < 19; i++)
815 {
816 moveto(i, 0);
817 prints("|");
818 moveto(i, 79);
819 prints("|");
820 }
821 moveto(19, 0);
822 prints("|-----------------------------------------------------------------------------|");
823 moveto(22, 0);
824 prints(" ----------------------------------------------------------------------------- ");
825 moveto(23, 0);
826 prints(" [\x1b[1;32mCtrl+C\x1b[m]退出");
827
828 iflush();
829
830 return 0;
831 }
832
833 static int bbsnet_selchange()
834 {
835 int i = bbsnet_menu.menu_item_pos[0];
836
837 moveto(20, 0);
838 clrtoeol();
839 prints("|\x1b[1m单位:\x1b[1;33m%-18s\x1b[m 站名:\x1b[1;33m%s\x1b[m",
840 bbsnet_conf[i].host2, bbsnet_conf[i].host1);
841 moveto(20, 79);
842 prints("|");
843 moveto(21, 0);
844 clrtoeol();
845 prints("|\x1b[1m连往:\x1b[1;33m%-20s", bbsnet_conf[i].ip);
846 if (bbsnet_conf[i].port != 23)
847 {
848 prints(" %d", bbsnet_conf[i].port);
849 }
850 prints("\x1b[m");
851 moveto(21, 79);
852 prints("|");
853 iflush();
854
855 return 0;
856 }
857
858 extern int bbs_net()
859 {
860 int ch, i;
861
862 load_bbsnet_conf(CONF_BBSNET);
863
864 clearscr();
865 bbsnet_refresh();
866 display_menu(&bbsnet_menu);
867 bbsnet_selchange();
868
869 while (!SYS_server_exit)
870 {
871 ch = igetch(100);
872
873 if (ch != KEY_NULL && ch != KEY_TIMEOUT)
874 {
875 BBS_last_access_tm = time(NULL);
876 }
877
878 switch (ch)
879 {
880 case KEY_NULL: // broken pipe
881 log_error("KEY_NULL\n");
882 goto cleanup;
883 case KEY_TIMEOUT:
884 if (time(NULL) - BBS_last_access_tm >= BBS_max_user_idle_time)
885 {
886 log_error("User input timeout\n");
887 goto cleanup;
888 }
889 continue;
890 case KEY_ESC:
891 case Ctrl('C'): // user cancel
892 goto cleanup;
893 case CR:
894 bbsnet_connect(bbsnet_menu.menu_item_pos[0]);
895 bbsnet_refresh();
896 display_menu(&bbsnet_menu);
897 bbsnet_selchange();
898 break;
899 case KEY_UP:
900 for (i = 0; i < STATION_PER_LINE; i++)
901 {
902 menu_control(&bbsnet_menu, KEY_UP);
903 }
904 bbsnet_selchange();
905 break;
906 case KEY_DOWN:
907 for (i = 0; i < STATION_PER_LINE; i++)
908 {
909 menu_control(&bbsnet_menu, KEY_DOWN);
910 }
911 bbsnet_selchange();
912 break;
913 case KEY_LEFT:
914 menu_control(&bbsnet_menu, KEY_UP);
915 bbsnet_selchange();
916 break;
917 case KEY_RIGHT:
918 menu_control(&bbsnet_menu, KEY_DOWN);
919 bbsnet_selchange();
920 break;
921 case KEY_HOME:
922 case KEY_PGUP:
923 menu_control(&bbsnet_menu, KEY_PGUP);
924 bbsnet_selchange();
925 break;
926 case KEY_END:
927 case KEY_PGDN:
928 menu_control(&bbsnet_menu, KEY_PGDN);
929 bbsnet_selchange();
930 break;
931 default:
932 menu_control(&bbsnet_menu, ch);
933 bbsnet_selchange();
934 break;
935 }
936 }
937
938 cleanup:
939 unload_bbsnet_conf();
940
941 return 0;
942 }

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