diff --git a/.gitignore b/.gitignore index 10270eb1..9e16ef63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ /conf/bbsd.conf +/conf/bbsnet.conf /conf/badwords.conf /conf/ssh_host_rsa_key* /conf/ssh_host_ed25519_key* +/conf/ssh_host_ecdsa_key* /utils/conf/db_conn.conf.php /aclocal.m4 /autom4te.cache diff --git a/INSTALL.md b/INSTALL.md index 59b81937..a787cccd 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -41,6 +41,7 @@ To install LBBS on Linux (e.g. Debian 13, CentOS Stream 10), please perform the 7) Modify following configuration files Default configuration files is saved as *.default, you should rename them first. $LBBS_HOME_DIR/conf/bbsd.conf + $LBBS_HOME_DIR/conf/bbsnet.conf $LBBS_HOME_DIR/conf/badwords.conf $LBBS_HOME_DIR/utils/conf/db_conn.conf.php @@ -53,6 +54,7 @@ To install LBBS on Linux (e.g. Debian 13, CentOS Stream 10), please perform the 10) Create SSH2 RSA / ED25519 certificate ssh-keygen -t rsa -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_rsa_key ssh-keygen -t ed25519 -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_ed25519_key + ssh-keygen -t ecdsa -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_ecdsa_key 11) Startup sudo -u bbs $LBBS_HOME_DIR/bin/bbsd diff --git a/INSTALL.zh_CN.md b/INSTALL.zh_CN.md index 0b19caae..659056d3 100644 --- a/INSTALL.zh_CN.md +++ b/INSTALL.zh_CN.md @@ -41,6 +41,7 @@ 6) 修改以下配置文件 默认配置文件被命名为*.default,请先将其改名。 $LBBS_HOME_DIR/conf/bbsd.conf + $LBBS_HOME_DIR/conf/bbsnet.conf $LBBS_HOME_DIR/conf/badwords.conf $LBBS_HOME_DIR/utils/conf/db_conn.conf.php @@ -53,6 +54,7 @@ 9) 创建SSH2 RSA / ED25519 证书 ssh-keygen -t rsa -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_rsa_key ssh-keygen -t ed25519 -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_ed25519_key + ssh-keygen -t ecdsa -C "Your Server Name" -f $LBBS_HOME_DIR/conf/ssh_host_ecdsa_key 10) 启动服务程序 sudo -u bbs $LBBS_HOME_DIR/bin/bbsd diff --git a/Makefile.am b/Makefile.am index 515643fd..8eacd35b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,8 +5,8 @@ SUBDIRS = utils src install-data-local: install -d $(prefix)/conf $(prefix)/data $(prefix)/log $(prefix)/var $(prefix)/var/articles $(prefix)/var/chicken $(prefix)/var/gen_ex - cd $(srcdir)/conf; find . -maxdepth 1 -type f -exec install -t $(prefix)/conf -m 644 {} \; - cd $(srcdir)/data; find . -maxdepth 1 -type f -exec install -t $(prefix)/data -m 644 {} \; + cd $(srcdir)/conf; find . -maxdepth 1 -type f -exec install -t $(prefix)/conf -m 644 -p {} \; + cd $(srcdir)/data; find . -maxdepth 1 -type f -exec install -t $(prefix)/data -m 644 -p {} \; find $(prefix)/ -type d -exec chmod 750 {} \; uninstall-local: diff --git a/README.md b/README.md index 4375dc63..f91b401f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Introduction ================= This software aims to providing a telnet-based interface for a pure web-based BBS [leafok_bbs](https://github.com/leafok/leafok_bbs). -Demo site : bbs.fenglin.info (Telnet 2323 / SSH2 2322) +Demo site : fenglin.info (Telnet 2323 / SSH2 2322) ![Welcome](misc/images/ssh_welcome.jpg "Welcome to LBBS") ![Menu](misc/images/telnet_menu.jpg "Main menu") ![Section](misc/images/telnet_section.jpg "List of articles") diff --git a/README.zh_CN.md b/README.zh_CN.md index 980707f1..c7b096b6 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -3,7 +3,7 @@ 简介 ================= 本软件旨在为一个纯Web访问的BBS [leafok_bbs](https://github.com/leafok/leafok_bbs) 提供基于Telnet访问的界面。 -演示站点 : bbs.fenglin.info (Telnet 2323 / SSH2 2322) +演示站点 : fenglin.info (Telnet 2323 / SSH2 2322) ![欢迎](misc/images/ssh_welcome.jpg "欢迎访问LBBS") ![菜单](misc/images/telnet_menu.jpg "主选单") ![版块](misc/images/telnet_section.jpg "文章列表") diff --git a/conf/bbsnet.conf b/conf/bbsnet.conf deleted file mode 100644 index b176a2b3..00000000 --- a/conf/bbsnet.conf +++ /dev/null @@ -1,6 +0,0 @@ -#单位 站名 位置 端口 编码 - -LBBS Testing localhost 2323 UTF-8 -清华大学 水木社区 bbs.newsmth.net 23 GBK -复旦大学 日月光华 bbs.fudan.edu.cn 2323 GBK -交通大学 饮水思源 bbs.sjtu.edu.cn 23 GBK diff --git a/conf/bbsnet.conf.default b/conf/bbsnet.conf.default new file mode 100644 index 00000000..f2f158a4 --- /dev/null +++ b/conf/bbsnet.conf.default @@ -0,0 +1,6 @@ +#单位 站名 位置 端口 SSH 编码 + +LBBS Testing localhost 2322 Y UTF-8 +清华大学 水木社区 bbs.newsmth.net 22 Y GBK +复旦大学 日月光华 bbs.fudan.edu.cn 2323 N GBK +交通大学 饮水思源 bbs.sjtu.edu.cn 23 N GBK diff --git a/conf/menu.conf b/conf/menu.conf index 0880d49a..b6e9bf5d 100644 --- a/conf/menu.conf +++ b/conf/menu.conf @@ -182,7 +182,8 @@ screen 10, 0, S_UNUSEABLE %menu M_SYSINFO title 0, 0, "系统资讯区" screen 10, 0, S_SYSINFO -@LICENSE 11, 5, 0, 0, "License", "(L) 【 使用执照 】" +@EULA 11, 5, 0, 0, "EULA", "(E) 【用户许可协议】" +@LICENSE 0, 0, 0, 0, "License", "(L) 【 使用执照 】" @COPYRIGHT 0, 0, 0, 0, "Copyright", "(C) 【 版权信息 】" @VERSION 0, 0, 0, 0, "Version", "(V) 【 版本记录 】" !.. 0, 0, 0, 0, "Back", "(B) 【 返回上一级 】" diff --git a/configure.ac b/configure.ac index 2e309021..88a93b6a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([lbbs],[1.6.1]) +AC_INIT([lbbs],[1.6.2]) AC_CONFIG_SRCDIR([src/]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([m4]) diff --git a/data/eula.txt b/data/eula.txt new file mode 100644 index 00000000..3376fb3c --- /dev/null +++ b/data/eula.txt @@ -0,0 +1,32 @@ +枫林在线(FengLin.info)《用户许可协议》 +(2025年12月2日 修订) + +1. 服务条款的确认和接纳 枫林在线的所有权和运作权归FengLin.info所有。枫林在线所提供的服务将完全按照其发布的章程、服务条款和操作规则严格执行。用户完成注册程序,即表示用户与枫林在线达成协议并接受所有的服务条款。 +2. 服务依赖的软硬件条件 枫林在线运用自己的软件系统通过互联网为用户提供网络服务,并承担服务条款和入网协议中对用户的责任和义务。用户必须: + 1) 自行配备上网所需的设备,包括个人电脑,手机、网络接入设备及其他必备的设备装置。 + 2) 自行负担上网所需的相关必要费用,如:电话费用、网络费用等。 +3. 用户必须提供详尽、真实和准确的个人资料,并对个人资料不断进行及时的更新。如果用户所提供的资料包含不正确的信息,枫林在线保留中止为该用户提供网络服务的权利。 +4. 服务条款的修改 枫林在线在必要时将修改服务条款,届时将在用户界面提示修改内容,如果用户不同意所作的修改,可以主动停止获得相应服务。如果用户希望继续享有枫林在线提供的服务,则必须接受服务条款的变动。 +5. 服务功能的变更 枫林在线保留随时修改服务,包括但不限于新增、移除、修改服务功能,临时中断服务,而不需事先通知用户的权利。 +6. 用户隐私制度 尊重用户隐私是枫林在线的一项基本政策。枫林在线将对用户资料实行保密,承诺不会在未经用户授权时公开、编辑或透露其注册资料及保存在枫林在线中的非公开内容,除非有法律明确要求枫林在线提供相关信息的情况存在。 +7. 用户帐号、密码和安全性 用户注册成为枫林在线的合法用户,须选定唯一的用户名,并自行设置密码。用户帐号仅限用户本人使用,不得将密码公开或提供给第三方。通过用户名和密码登陆后的一切操作,均视为用户本人的行为。用户须妥善保管密码,并定期修改。如发现可疑帐号活动,请立即通知枫林在线。 +8. 免责声明 枫林在线对任何直接、间接、偶然、特殊及继起的损害不负责任,这些损害可能来自:不正当使用网络服务,在网上购买商品或进行同类型服务,在网上进行交易,非法使用网络服务或用户传送的信息有所变动。这些行为都有可能会导致枫林在线的形象受损,所以枫林在线事先提出这种损害的可能性。 +9. 服务稳定性 鉴于枫林在线向用户提供的服务不收取任何费用,枫林在线不对服务稳定性作出任何承诺。枫林在线建议用户自行备份在本网站上存放的重要数据,包括但不限于发表的文章、发送的消息、上传的文件等。因发生如火灾、水灾、暴动、骚乱、战争、自然灾害等不可抗事故,枫林在线所不能控制的时间而影响枫林在线提供服务,造成服务中断、数据丢失等后果,枫林在线无须承担任何责任。 +10. 版权保护 枫林在线用户的文章版权归原文作者和枫林在线共同所有,转贴的文章枫林在线不拥有版权。枫林在线有权在本网站范围内引用、发布、转载用户在枫林在线社区发布、转贴的内容。用户在枫林在线上发布信息,应当遵守相关法律法规,尊重他人的版权,并对由此所引发的版权异议、纠纷承担法律责任。任何用户需要转载枫林在线文章,必须征得原文作者或枫林在线同意或授权,并应注明作者名称和真实出处。 +11. 用户管理 用户单独承担发布内容的责任。用户对服务的使用是根据所有适用于枫林在线的法律、法规和政府颁布的管理办法。 用户必须遵循: + 1) 从中国境内向外传输重要数据(包括但不限于个人信息、技术性资料等)时,必须符合中国有关法律、法规和政府规章; + 2) 不得使用网络服务以作非法用途; + 3) 不得干扰、混乱、非法侵入网络服务及其计算机系统; + 4) 遵守所有使用网络服务的网络协议、规定、程序和惯例; + 5) 不能传输任何非法的、骚扰性的、中伤他人的、辱骂性的、恐吓性的、伤害性的、庸俗的、淫秽的等信息资料; + 6) 不能传输任何教唆他人构成犯罪行为的资料; + 7) 不能传输任何可能涉及或危害国家安全的资料。 + 若用户的行为不符合以上提到的服务条款,枫林在线将立即停止用户帐号的服务。枫林在线的系统记录有可能作为用户违反法律的证据。 +12. 保障 用户同意保障和维护枫林在线全体成员的利益,负责支付由用户使用超出服务范围引起的律师费用,违反服务条款的损害补偿费用等。 +13. 结束服务 用户或枫林在线可随时根据用户管理的规范(参见第11条)和实际情况中断一项或多项服务,枫林在线无须对个人或第三方负责。用户对后来的条款修改有异议,或对枫林在线的服务不满,可以行使如下权利: + 1) 停止使用枫林在线的网络服务。 + 2) 通告枫林在线停止对该用户的服务。结束用户服务后,用户使用相应服务的权利立即中止。在法律规定的数据保存期限之后,枫林在线不对用户承担任何义务和责任。 +14. 通告 所有发给用户的通告都可以通过重要页面的公告或电子邮件传送。 +15. 广告策划 用户在他们发表的信息中加入宣传资料或广告策划,在枫林在线的免费服务上展示他们的产品,任何这类促销方法,包括运输货物、付款、服务、商业条件、担保及与广告有关的描述都只是在相应的用户和广告销售商之间发生。枫林在线不为此类行为提供任何担保,也不为其后果承担任何责任。 +16. 网络服务的内容所有权 枫林在线定义的内容包括:文字、软件、声音、相片、录像、图表;广告中的全部内容;电子邮件中的全部内容;枫林在线为用户提供的其他信息。所有这些信息均受版权、商标、标签和其他财产所有权法律的保护。用户只能在枫林在线和广告商的授权下才能使用这些内容,不能擅自复制、再造或创造与这些内容有关的派生产品。枫林在线的所有内容版权归原文作者和枫林在线共同所有,任何人需要转载枫林在线的内容,必须获得原文作者或枫林在线的授权。 +17. 法律 本服务条款要与中华人民共和国的法律解释相一致,用户和枫林在线一致同意服从高等法院的所有管辖。如果任何服务条款与法律相抵触,则这些条款将按法律规定重新解释,而其他条款依旧保持对枫林在线和用户产生法律效力和影响。 diff --git a/data/register.txt b/data/register.txt index 59fa1541..6f4499c2 100644 --- a/data/register.txt +++ b/data/register.txt @@ -1,4 +1,4 @@  该功能尚未完成,请使用WWW方式注册新帐号。 - https://www.fenglin.info/bbs/user_reg.php + https://fenglin.info/bbs/user_reg.php  diff --git a/data/welcome.txt b/data/welcome.txt index 7e98ca8b..30cd9309 100644 --- a/data/welcome.txt +++ b/data/welcome.txt @@ -16,5 +16,5 @@ ▍ ▎ ▏ ▍ ▆   ▎ ▏ ▌   ▁▃▅ ▆▃▁  -  ◥▃▇▅▅▇ Web https://www.fenglin.info - ▁▂▄◤ ▆▄◣ SSH bbs.fenglin.info:2322 +  ◥▃▇▅▅▇ Web https://fenglin.info + ▁▂▄◤ ▆▄◣ SSH fenglin.info:2322 diff --git a/include/bbs.h b/include/bbs.h index e759d0f0..bc20f850 100644 --- a/include/bbs.h +++ b/include/bbs.h @@ -63,6 +63,9 @@ extern char BBS_nickname[BBS_nickname_max_len + 1]; extern char BBS_user_tz[BBS_user_tz_max_len + 1]; extern int BBS_user_exp; +extern time_t BBS_eula_tm; +extern int BBS_update_eula; + extern time_t BBS_login_tm; extern time_t BBS_last_access_tm; diff --git a/include/common.h b/include/common.h index f21da9be..2b75e606 100644 --- a/include/common.h +++ b/include/common.h @@ -44,6 +44,8 @@ extern const char CONF_BWF[]; extern const char CONF_TOP10_MENU[]; extern const char SSH_HOST_RSA_KEY_FILE[]; extern const char SSH_HOST_ED25519_KEY_FILE[]; +extern const char SSH_HOST_ECDSA_KEY_FILE[]; +extern const char SSH_KNOWN_HOSTS_FILE[]; extern const char LOG_FILE_INFO[]; extern const char LOG_FILE_ERROR[]; @@ -51,6 +53,7 @@ extern const char LOG_FILE_ERROR[]; extern const char DATA_WELCOME[]; extern const char DATA_REGISTER[]; extern const char DATA_GOODBYE[]; +extern const char DATA_EULA[]; extern const char DATA_LICENSE[]; extern const char DATA_COPYRIGHT[]; extern const char DATA_VERSION[]; diff --git a/include/login.h b/include/login.h index d165c0f4..e563a802 100644 --- a/include/login.h +++ b/include/login.h @@ -29,4 +29,6 @@ extern int user_online_exp(MYSQL *db); extern int user_online_update(const char *action); +extern int user_update_agreement(void); + #endif //_LOGIN_H_ diff --git a/include/menu_proc.h b/include/menu_proc.h index 3c20f3c9..6036bdff 100644 --- a/include/menu_proc.h +++ b/include/menu_proc.h @@ -12,6 +12,7 @@ extern int list_section(void *param); extern int exec_mbem(void *param); extern int exit_bbs(void *param); +extern int eula(void *param); extern int license(void *param); extern int copyright(void *param); extern int version(void *param); diff --git a/src/article_favor_display.c b/src/article_favor_display.c index 4bf92c6c..d94f087c 100644 --- a/src/article_favor_display.c +++ b/src/article_favor_display.c @@ -161,6 +161,12 @@ static enum select_cmd_t article_favor_select(int total_page, int item_count, in if (ch != KEY_NULL && ch != KEY_TIMEOUT) { BBS_last_access_tm = time(NULL); + + // Refresh current action + if (user_online_update(NULL) < 0) + { + log_error("user_online_update(NULL) error\n"); + } } switch (ch) diff --git a/src/bbs.c b/src/bbs.c index 1d333679..baf0a61e 100644 --- a/src/bbs.c +++ b/src/bbs.c @@ -47,6 +47,9 @@ char BBS_nickname[BBS_nickname_max_len + 1]; char BBS_user_tz[BBS_user_tz_max_len + 1]; int BBS_user_exp; +time_t BBS_eula_tm; +int BBS_update_eula = 0; + time_t BBS_login_tm; time_t BBS_last_access_tm; diff --git a/src/bbs_cmd.c b/src/bbs_cmd.c index 5401966f..7445c91f 100644 --- a/src/bbs_cmd.c +++ b/src/bbs_cmd.c @@ -20,6 +20,7 @@ static const BBS_CMD bbs_cmd_list[] = { {"LIST_SECTION", list_section}, {"RunMBEM", exec_mbem}, {"EXITBBS", exit_bbs}, + {"EULA", eula}, {"LICENSE", license}, {"COPYRIGHT", copyright}, {"VERSION", version}, diff --git a/src/bbs_main.c b/src/bbs_main.c index d6fb3923..d697ea85 100644 --- a/src/bbs_main.c +++ b/src/bbs_main.c @@ -118,11 +118,6 @@ int bbs_logout(void) return -2; } - if (user_online_del(db) < 0) - { - return -3; - } - mysql_close(db); display_file(DATA_GOODBYE, 1); @@ -133,6 +128,26 @@ int bbs_logout(void) return 0; } +int bbs_session_cleanup(void) +{ + MYSQL *db; + + db = db_open(); + if (db == NULL) + { + return -1; + } + + if (user_online_del(db) < 0) + { + return -2; + } + + mysql_close(db); + + return 0; +} + int bbs_center() { int ch; @@ -370,6 +385,13 @@ int bbs_main() log_common("User [%s] (uid=%d) login from %s:%d\n", BBS_username, BBS_priv.uid, hostaddr_client, port_client); + // Check EULA update status + if (BBS_update_eula) + { + user_update_agreement(); + goto cleanup; + } + // Load section aid locations if (section_aid_locations_load(BBS_priv.uid) < 0) { @@ -406,6 +428,9 @@ int bbs_main() // Main bbs_center(); + // Logout + bbs_logout(); + // Save section aid locations if (section_aid_locations_save(BBS_priv.uid) < 0) { @@ -425,8 +450,8 @@ int bbs_main() } cleanup: - // Logout - bbs_logout(); + // Cleanup session + bbs_session_cleanup(); // Cleanup iconv io_conv_cleanup(); diff --git a/src/bbs_net.c b/src/bbs_net.c index 0f12f09c..737cdf3e 100644 --- a/src/bbs_net.c +++ b/src/bbs_net.c @@ -18,6 +18,7 @@ #include "login.h" #include "menu.h" #include "screen.h" +#include "str_process.h" #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -50,14 +52,17 @@ enum _bbs_net_constant_t MAX_PROCESS_BAR_LEN = 30, MAXSTATION = 26 * 2, STATION_PER_LINE = 4, + USERNAME_MAX_LEN = 20, + PASSWORD_MAX_LEN = 20, }; struct _bbsnet_conf { - char host1[20]; - char host2[40]; - char ip[40]; + char org_name[40]; + char site_name[40]; + char host_name[IP_ADDR_LEN]; in_port_t port; + int8_t use_ssh; char charset[CHARSET_MAX_LEN + 1]; } bbsnet_conf[MAXSTATION]; @@ -69,7 +74,7 @@ static int load_bbsnet_conf(const char *file_config) MENU *p_menu; MENU_ITEM *p_menu_item; MENU_ITEM_ID menu_item_id; - char t[256], *t1, *t2, *t3, *t4, *t5, *saveptr; + char line[LINE_BUFFER_LEN], *t1, *t2, *t3, *t4, *t5, *t6, *saveptr; fp = fopen(file_config, "r"); if (fp == NULL) @@ -101,27 +106,30 @@ static int load_bbsnet_conf(const char *file_config) p_menu->screen_show = 0; menu_item_id = 0; - while (fgets(t, 255, fp) && menu_item_id < MAXSTATION) + while (fgets(line, sizeof(line), fp) && menu_item_id < MAXSTATION) { - t1 = strtok_r(t, MENU_CONF_DELIM, &saveptr); + t1 = strtok_r(line, MENU_CONF_DELIM, &saveptr); t2 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr); t3 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr); t4 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr); t5 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr); + t6 = strtok_r(NULL, MENU_CONF_DELIM, &saveptr); - if (t1 == NULL || t2 == NULL || t3 == NULL || t4 == NULL || t5 == NULL || t[0] == '#' || t[0] == '*') + if (t1 == NULL || t2 == NULL || t3 == NULL || t4 == NULL || + t5 == NULL || t6 == NULL || line[0] == '#' || line[0] == '*') { continue; } - strncpy(bbsnet_conf[menu_item_id].host1, t2, sizeof(bbsnet_conf[menu_item_id].host1) - 1); - bbsnet_conf[menu_item_id].host1[sizeof(bbsnet_conf[menu_item_id].host1) - 1] = '\0'; - strncpy(bbsnet_conf[menu_item_id].host2, t1, sizeof(bbsnet_conf[menu_item_id].host2) - 1); - bbsnet_conf[menu_item_id].host2[sizeof(bbsnet_conf[menu_item_id].host2) - 1] = '\0'; - strncpy(bbsnet_conf[menu_item_id].ip, t3, sizeof(bbsnet_conf[menu_item_id].ip) - 1); - bbsnet_conf[menu_item_id].ip[sizeof(bbsnet_conf[menu_item_id].ip) - 1] = '\0'; + strncpy(bbsnet_conf[menu_item_id].site_name, t2, sizeof(bbsnet_conf[menu_item_id].site_name) - 1); + bbsnet_conf[menu_item_id].site_name[sizeof(bbsnet_conf[menu_item_id].site_name) - 1] = '\0'; + strncpy(bbsnet_conf[menu_item_id].org_name, t1, sizeof(bbsnet_conf[menu_item_id].org_name) - 1); + bbsnet_conf[menu_item_id].org_name[sizeof(bbsnet_conf[menu_item_id].org_name) - 1] = '\0'; + strncpy(bbsnet_conf[menu_item_id].host_name, t3, sizeof(bbsnet_conf[menu_item_id].host_name) - 1); + bbsnet_conf[menu_item_id].host_name[sizeof(bbsnet_conf[menu_item_id].host_name) - 1] = '\0'; bbsnet_conf[menu_item_id].port = (in_port_t)(t4 ? atoi(t4) : 23); - strncpy(bbsnet_conf[menu_item_id].charset, t5, sizeof(bbsnet_conf[menu_item_id].charset) - 1); + bbsnet_conf[menu_item_id].use_ssh = (toupper(t5[0]) == 'Y'); + strncpy(bbsnet_conf[menu_item_id].charset, t6, sizeof(bbsnet_conf[menu_item_id].charset) - 1); bbsnet_conf[menu_item_id].charset[sizeof(bbsnet_conf[menu_item_id].charset) - 1] = '\0'; p_menu_item = get_menu_item_by_id(&bbsnet_menu, menu_item_id); @@ -141,7 +149,7 @@ static int load_bbsnet_conf(const char *file_config) (char)(menu_item_id < MAXSTATION / 2 ? 'A' + menu_item_id : 'a' + menu_item_id); p_menu_item->name[1] = '\0'; snprintf(p_menu_item->text, sizeof(p_menu_item->text), "%c. %s", - p_menu_item->name[0], bbsnet_conf[menu_item_id].host1); + p_menu_item->name[0], bbsnet_conf[menu_item_id].site_name); p_menu->items[p_menu->item_count] = menu_item_id; p_menu->item_count++; @@ -187,7 +195,7 @@ static void process_bar(int n, int len) n = len; } - moveto(4, 0); + moveto(4, 1); prints(" ------------------------------ \r\n"); snprintf(buf, sizeof(buf), " %3d%% ", n * 100 / len); memcpy(buf2, buf, (size_t)n); @@ -244,20 +252,65 @@ static int bbsnet_connect(int n) time_t t_used = time(NULL); struct tm *tm_used; int ch; + char remote_user[USERNAME_MAX_LEN + 1]; + char remote_pass[PASSWORD_MAX_LEN + 1]; + ssh_session session = NULL; + ssh_channel channel = NULL; + int ssh_process_config = 0; + int ssh_log_level = SSH_LOG_NOLOG; if (user_online_update("BBS_NET") < 0) { log_error("user_online_update(BBS_NET) error\n"); } + if (bbsnet_conf[n].use_ssh) + { + clearscr(); + + if (!SSH_v2) + { + moveto(1, 1); + prints("只有在以SSH方式登陆本站时,才能使用SSH站点穿梭。"); + press_any_key(); + return 0; + } + + moveto(1, 1); + prints("通过SSH方式连接[%s]...", bbsnet_conf[n].site_name); + moveto(2, 1); + prints("请输入用户名: "); + iflush(); + if (str_input(remote_user, sizeof(remote_user), DOECHO) < 0) + { + return -1; + } + if (remote_user[0] == '\0') + { + return 0; + } + + moveto(3, 1); + prints("请输入密码: "); + iflush(); + if (str_input(remote_pass, sizeof(remote_pass), NOECHO) < 0) + { + return -1; + } + if (remote_pass[0] == '\0') + { + return 0; + } + } + clearscr(); - moveto(0, 0); + moveto(1, 1); prints("\033[1;32m正在测试往 %s (%s) 的连接,请稍候... \033[m\r\n", - bbsnet_conf[n].host1, bbsnet_conf[n].ip); + bbsnet_conf[n].site_name, bbsnet_conf[n].host_name); iflush(); - p_host = gethostbyname(bbsnet_conf[n].ip); + p_host = gethostbyname(bbsnet_conf[n].host_name); if (p_host == NULL) { @@ -417,7 +470,10 @@ static int bbsnet_connect(int n) else if (pfds[i].fd == STDIN_FILENO && (pfds[i].revents & POLLIN)) #endif { - ch = igetch(0); + do + { + ch = igetch(0); + } while (ch == 0); if (ch == Ctrl('C') || ch == KEY_ESC) { goto cleanup; @@ -455,6 +511,158 @@ static int bbsnet_connect(int n) local_addr[sizeof(local_addr) - 1] = '\0'; local_port = ntohs(sin.sin_port); + if (bbsnet_conf[n].use_ssh) + { + session = ssh_new(); + if (session == NULL) + { + log_error("ssh_new() error\n"); + goto cleanup; + } + + if (ssh_options_set(session, SSH_OPTIONS_FD, &sock) < 0 || + ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &ssh_process_config) < 0 || + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, SSH_KNOWN_HOSTS_FILE) < 0 || + ssh_options_set(session, SSH_OPTIONS_HOST, bbsnet_conf[n].host_name) < 0 || + ssh_options_set(session, SSH_OPTIONS_USER, remote_user) < 0 || + ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "+ssh-rsa") < 0 || + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0) + { + log_error("Error setting SSH options: %s\n", ssh_get_error(session)); + goto cleanup; + } + + ssh_set_blocking(session, 0); + + while (!SYS_server_exit) + { + ret = ssh_connect(session); + if (ret == SSH_OK) + { + break; + } + else if (ret == SSH_ERROR) + { + log_error("ssh_connect() error\n"); + goto cleanup; + } + } + + ret = ssh_session_is_known_server(session); + switch (ret) + { + case SSH_KNOWN_HOSTS_NOT_FOUND: + case SSH_KNOWN_HOSTS_UNKNOWN: + if (ssh_session_update_known_hosts(session) != SSH_OK) + { + log_error("ssh_session_update_known_hosts(%s) error\n", bbsnet_conf[n].host_name); + prints("\033[1;31m无法添加服务器证书\033[m"); + press_any_key(); + goto cleanup; + } + log_common("SSH key of (%s) is added into %s\n", bbsnet_conf[n].host_name, SSH_KNOWN_HOSTS_FILE); + case SSH_KNOWN_HOSTS_OK: + break; + case SSH_KNOWN_HOSTS_CHANGED: + case SSH_KNOWN_HOSTS_OTHER: + log_error("ssh_session_is_known_server(%s) error: %d\n", bbsnet_conf[n].host_name, ret); + prints("\033[1;31m服务器证书已变更\033[m"); + press_any_key(); + goto cleanup; + } + + for (int i = 0; !SYS_server_exit;) + { + ret = ssh_userauth_password(session, NULL, remote_pass); + if (ret == SSH_AUTH_SUCCESS) + { + break; + } + else if (ret == SSH_AUTH_AGAIN) + { +#ifdef _DEBUG + log_error("ssh_userauth_password() error: SSH_AUTH_AGAIN\n"); +#endif + } + else if (ret == SSH_AUTH_ERROR) + { + log_error("ssh_userauth_password() error: %d\n", ret); + goto cleanup; + } + else // if (ret == SSH_AUTH_DENIED) + { + prints("\033[1;31m身份验证失败!\033[m\r\n"); + i++; + if (i < BBS_login_retry_times) + { + prints("请输入密码: "); + iflush(); + if (str_input(remote_pass, sizeof(remote_pass), NOECHO) < 0) + { + goto cleanup; + } + if (remote_pass[0] == '\0') + { + goto cleanup; + } + } + else + { + goto cleanup; + } + } + } + + channel = ssh_channel_new(session); + if (channel == NULL) + { + log_error("ssh_channel_new() error\n"); + goto cleanup; + } + + while (!SYS_server_exit) + { + ret = ssh_channel_open_session(channel); + if (ret == SSH_OK) + { + break; + } + else if (ret == SSH_ERROR) + { + log_error("ssh_channel_open_session() error\n"); + goto cleanup; + } + } + + while (!SYS_server_exit) + { + ret = ssh_channel_request_pty(channel); + if (ret == SSH_OK) + { + break; + } + else if (ret == SSH_ERROR) + { + log_error("ssh_channel_request_pty() error\n"); + goto cleanup; + } + } + + while (!SYS_server_exit) + { + ret = ssh_channel_request_shell(channel); + if (ret == SSH_OK) + { + break; + } + else if (ret == SSH_ERROR) + { + log_error("ssh_channel_request_shell() error\n"); + goto cleanup; + } + } + } + prints("\033[1;31m连接成功!\033[m\r\n"); iflush(); log_common("BBSNET connect to %s:%d from %s:%d by [%s]\n", @@ -509,6 +717,13 @@ static int bbsnet_connect(int n) break; } + if (bbsnet_conf[n].use_ssh && ssh_channel_is_closed(channel)) + { + log_error("Remote SSH channel is closed\n"); + loop = 0; + break; + } + #ifdef HAVE_SYS_EPOLL_H nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second ret = nfds; @@ -584,7 +799,7 @@ static int bbsnet_connect(int n) if (events[i].data.fd == STDOUT_FILENO) #else if (pfds[i].fd == STDOUT_FILENO && (pfds[i].revents & POLLOUT)) -#endif +#endif { stdout_write_wait = 1; } @@ -697,7 +912,20 @@ static int bbsnet_connect(int n) while (input_conv_offset < input_conv_len && !SYS_server_exit) { - ret = (int)write(sock, input_conv + input_conv_offset, (size_t)(input_conv_len - input_conv_offset)); + if (bbsnet_conf[n].use_ssh) + { + ret = ssh_channel_write(channel, input_conv + input_conv_offset, (uint32_t)(input_conv_len - input_conv_offset)); + if (ret == SSH_ERROR) + { + log_error("ssh_channel_write() error: %s\n", ssh_get_error(session)); + loop = 0; + break; + } + } + else + { + ret = (int)write(sock, input_conv + input_conv_offset, (size_t)(input_conv_len - input_conv_offset)); + } if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) @@ -743,7 +971,32 @@ static int bbsnet_connect(int n) { while (output_buf_len < sizeof(output_buf) && !SYS_server_exit) { - ret = (int)read(sock, output_buf + output_buf_len, sizeof(output_buf) - (size_t)output_buf_len); + if (bbsnet_conf[n].use_ssh) + { + ret = ssh_channel_read_nonblocking(channel, output_buf + output_buf_len, + (uint32_t)(sizeof(output_buf) - (size_t)output_buf_len), 0); + if (ret == SSH_ERROR) + { + log_error("ssh_channel_read_nonblocking() error: %s\n", ssh_get_error(session)); + loop = 0; + break; + } + else if (ret == SSH_EOF) + { + sock_read_wait = 0; + loop = 0; + break; + } + else if (ret == 0) + { + sock_read_wait = 0; + break; + } + } + else + { + ret = (int)read(sock, output_buf + output_buf_len, sizeof(output_buf) - (size_t)output_buf_len); + } if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) @@ -860,6 +1113,13 @@ static int bbsnet_connect(int n) } #endif + if (bbsnet_conf[n].use_ssh) + { + ssh_channel_free(channel); + ssh_disconnect(session); + ssh_free(session); + } + // Restore STDIN/STDOUT flags fcntl(STDIN_FILENO, F_SETFL, flags_stdin); fcntl(STDOUT_FILENO, F_SETFL, flags_stdout); @@ -884,21 +1144,22 @@ static int bbsnet_connect(int n) static int bbsnet_refresh() { clearscr(); - moveto(1, 0); - prints(" ----------------------------------------------------------------------------- "); + + moveto(1, 1); + prints(" ------------------------------------------------------------------------------ "); for (int i = 2; i < 19; i++) { - moveto(i, 0); + moveto(i, 1); prints("|"); - moveto(i, 79); + moveto(i, 80); prints("|"); } - moveto(19, 0); - prints("|-----------------------------------------------------------------------------|"); - moveto(22, 0); - prints(" ----------------------------------------------------------------------------- "); - moveto(23, 0); - prints(" [\x1b[1;32mCtrl+C\x1b[m]退出"); + moveto(19, 1); + prints("|------------------------------------------------------------------------------|"); + moveto(22, 1); + prints(" ------------------------------------------------------------------------------ "); + moveto(23, 1); + prints(" [\033[1;32mCtrl+C\033[m]退出"); iflush(); @@ -909,21 +1170,19 @@ static int bbsnet_selchange() { int i = bbsnet_menu.menu_item_pos[0]; - moveto(20, 0); + moveto(20, 1); clrtoeol(); - prints("|\x1b[1m单位:\x1b[1;33m%-18s\x1b[m 站名:\x1b[1;33m%s\x1b[m", - bbsnet_conf[i].host2, bbsnet_conf[i].host1); - moveto(20, 79); + prints("|\033[1m单位: \033[1;33m%s\033[m%*s 站名: \033[1;33m%s\033[m%*s 类型: \033[1;33m%s\033[m", + bbsnet_conf[i].org_name, 20 - str_length(bbsnet_conf[i].org_name, 1), "", + bbsnet_conf[i].site_name, 20 - str_length(bbsnet_conf[i].site_name, 1), "", + (bbsnet_conf[i].use_ssh ? "SSH" : "Telnet")); + moveto(20, 80); prints("|"); - moveto(21, 0); + moveto(21, 1); clrtoeol(); - prints("|\x1b[1m连往:\x1b[1;33m%-20s", bbsnet_conf[i].ip); - if (bbsnet_conf[i].port != 23) - { - prints(" %d", bbsnet_conf[i].port); - } - prints("\x1b[m"); - moveto(21, 79); + prints("|\033[1m连往: \033[1;33m%-20s\033[m 端口: \033[1;33m%-5d\033[m 编码: \033[1;33m%s\033[m", + bbsnet_conf[i].host_name, bbsnet_conf[i].port, bbsnet_conf[i].charset); + moveto(21, 80); prints("|"); iflush(); @@ -936,7 +1195,6 @@ int bbs_net() load_bbsnet_conf(CONF_BBSNET); - clearscr(); bbsnet_refresh(); display_menu(&bbsnet_menu); bbsnet_selchange(); @@ -967,6 +1225,10 @@ int bbs_net() goto cleanup; case CR: bbsnet_connect(bbsnet_menu.menu_item_pos[0]); + // Force cleanup anything remaining in the output buffer + clearscr(); + iflush(); + // Clear screen and redraw menu bbsnet_refresh(); display_menu(&bbsnet_menu); bbsnet_selchange(); diff --git a/src/common.c b/src/common.c index da3ca091..2d8459ac 100644 --- a/src/common.c +++ b/src/common.c @@ -28,6 +28,8 @@ const char CONF_BWF[] = "conf/badwords.conf"; const char CONF_TOP10_MENU[] = "var/bbs_top_menu.conf"; const char SSH_HOST_RSA_KEY_FILE[] = "conf/ssh_host_rsa_key"; const char SSH_HOST_ED25519_KEY_FILE[] = "conf/ssh_host_ed25519_key"; +const char SSH_HOST_ECDSA_KEY_FILE[] = "conf/ssh_host_ecdsa_key"; +const char SSH_KNOWN_HOSTS_FILE[] = "var/known_hosts"; const char LOG_FILE_INFO[] = "log/bbsd.log"; const char LOG_FILE_ERROR[] = "log/error.log"; @@ -35,6 +37,7 @@ const char LOG_FILE_ERROR[] = "log/error.log"; const char DATA_WELCOME[] = "data/welcome.txt"; const char DATA_REGISTER[] = "data/register.txt"; const char DATA_GOODBYE[] = "data/goodbye.txt"; +const char DATA_EULA[] = "data/eula.txt"; const char DATA_LICENSE[] = "data/license.txt"; const char DATA_COPYRIGHT[] = "data/copyright.txt"; const char DATA_VERSION[] = "data/version.txt"; @@ -59,6 +62,7 @@ const char *data_files_load_startup[] = { DATA_WELCOME, DATA_REGISTER, DATA_GOODBYE, + DATA_EULA, DATA_LICENSE, DATA_COPYRIGHT, DATA_VERSION, diff --git a/src/login.c b/src/login.c index 1f609086..5d569dd1 100644 --- a/src/login.c +++ b/src/login.c @@ -43,6 +43,7 @@ int bbs_login(void) char password[BBS_password_max_len + 1]; int i = 0; int ok = 0; + int ret; for (; !SYS_server_exit && !ok && i < BBS_login_retry_times; i++) { @@ -79,7 +80,14 @@ int bbs_login(void) continue; } - ok = (check_user(username, password) == 0); + ret = check_user(username, password); + if (ret == 2) // Enforce update user agreement + { + BBS_update_eula = 1; + ret = 0; + } + + ok = (ret == 0); iflush(); } } @@ -316,6 +324,13 @@ int check_user(const char *username, const char *password) goto cleanup; } + if (!SSH_v2 && checklevel2(&BBS_priv, P_MAN_S)) + { + prints("\033[1;31m非普通账户必须使用SSH方式登录\033[m\r\n"); + ret = 1; + goto cleanup; + } + ret = load_user_info(db, BBS_uid); switch (ret) @@ -326,9 +341,8 @@ int check_user(const char *username, const char *password) prints("\033[1;31m读取用户数据错误...\033[m\r\n"); ret = -1; goto cleanup; - case -2: // Unused - prints("\033[1;31m请通过Web登录更新用户许可协议...\033[m\r\n"); - ret = 1; + case -2: // Enforce update user agreement + ret = 2; goto cleanup; case -3: // Dead prints("\033[1;31m很遗憾,您已经永远离开了我们的世界!\033[m\r\n"); @@ -339,13 +353,6 @@ int check_user(const char *username, const char *password) goto cleanup; } - if (!SSH_v2 && checklevel2(&BBS_priv, P_MAN_S)) - { - prints("\033[1;31m非普通账户必须使用SSH方式登录\033[m\r\n"); - ret = 1; - goto cleanup; - } - snprintf(sql, sizeof(sql), "UPDATE user_pubinfo SET visit_count = visit_count + 1, " "last_login_dt = NOW() WHERE UID = %d", @@ -443,7 +450,13 @@ int load_user_info(MYSQL *db, int BBS_uid) if (load_priv(db, &BBS_priv, BBS_uid) != 0) { - ret = -1; + ret = -1; // Data not found + goto cleanup; + } + + if (last_login_dt < BBS_eula_tm) + { + ret = -2; // require update agreement first goto cleanup; } @@ -601,3 +614,80 @@ int user_online_update(const char *action) return 1; } + +int user_update_agreement(void) +{ + MYSQL *db = NULL; + char sql[SQL_BUFFER_LEN]; + int ch; + int ret = 0; + + clearscr(); + moveto(2, 1); + prints("尊敬的用户:"); + moveto(3, 1); + prints(" 本站的《用户许可协议》已更新,您必须在仔细阅读并接受后,才能继续使用本站提供的服务。"); + press_any_key(); + + clearscr(); + display_file(DATA_EULA, 1); + + while (!SYS_server_exit) + { + clearscr(); + moveto(2, 1); + + ch = press_any_key_ex("您是否接受本站的《用户许可协议》? (A)接受, (D)拒绝, (R)再看看协议 [D]", 60); + switch (toupper(ch)) + { + case KEY_NULL: + return -1; + case KEY_TIMEOUT: + case CR: + case 'D': + moveto(3, 1); + prints("您已拒绝《用户许可协议》,再见!"); + press_any_key(); + return 0; + case 'R': + clearscr(); + display_file(DATA_EULA, 1); + continue; + case 'A': + db = db_open(); + if (db == NULL) + { + ret = -1; + goto cleanup; + } + + snprintf(sql, sizeof(sql), + "UPDATE user_pubinfo SET visit_count = visit_count + 1, " + "last_login_dt = NOW() WHERE UID = %d", + BBS_priv.uid); + if (mysql_query(db, sql) != 0) + { + log_error("Update user_pubinfo error: %s\n", mysql_error(db)); + ret = -1; + goto cleanup; + } + + mysql_close(db); + db = NULL; + + moveto(3, 1); + prints("您已接受《用户许可协议》,请重新登陆。"); + press_any_key(); + return 1; + default: + continue; + } + + break; + } + +cleanup: + mysql_close(db); + + return ret; +} diff --git a/src/main.c b/src/main.c index 2436ba63..36e1be18 100644 --- a/src/main.c +++ b/src/main.c @@ -106,6 +106,7 @@ int main(int argc, char *argv[]) struct sigaction act = {0}; int i; int j; + struct stat file_stat; // Parse args for (i = 1; i < argc; i++) @@ -231,6 +232,14 @@ int main(int argc, char *argv[]) return -2; } + // Get EULA modification tm + if (stat(DATA_EULA, &file_stat) == -1) + { + log_error("stat(%s) error\n", DATA_EULA, errno); + goto cleanup; + } + BBS_eula_tm = file_stat.st_mtim.tv_sec; + // Check article cache dir ret = mkdir(VAR_ARTICLE_CACHE_DIR, 0750); if (ret == -1 && errno != EEXIST) diff --git a/src/menu_proc.c b/src/menu_proc.c index aab58886..baf39d8f 100644 --- a/src/menu_proc.c +++ b/src/menu_proc.c @@ -125,6 +125,13 @@ int exit_bbs(void *param) return EXITBBS; } +int eula(void *param) +{ + display_file(DATA_EULA, 0); + + return REDRAW; +} + int license(void *param) { display_file(DATA_LICENSE, 0); @@ -299,6 +306,11 @@ int show_top10_menu(void *param) BBS_last_access_tm = time(NULL); } + if (user_online_update("TOP10_MENU") < 0) + { + log_error("user_online_update(TOP10_MENU) error\n"); + } + switch (ch) { case KEY_NULL: // broken pipe diff --git a/src/net_server.c b/src/net_server.c index 57df0556..2c726b1b 100644 --- a/src/net_server.c +++ b/src/net_server.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,11 @@ static int auth_password(ssh_session session, const char *user, else { ret = check_user(user, password); + if (ret == 2) // Enforce update user agreement + { + BBS_update_eula = 1; + ret = 0; + } } if (ret == 0) @@ -422,6 +428,8 @@ static int fork_server(void) log_error("Error setting SSH options: %s\n", ssh_get_error(SSH_session)); goto cleanup; } + + ssh_set_blocking(SSH_session, 0); } // Redirect Input @@ -507,6 +515,7 @@ static int fork_server(void) int net_server(const char *hostaddr, in_port_t port[]) { + struct stat file_stat; unsigned int addrlen; int ret; int flags_server[2]; @@ -536,7 +545,7 @@ int net_server(const char *hostaddr, in_port_t port[]) if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0) { - log_error("Error setting SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE); + log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE); } else { @@ -544,7 +553,15 @@ int net_server(const char *hostaddr, in_port_t port[]) } if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0) { - log_error("Error setting SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE); + log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE); + } + else + { + ssh_key_valid = 1; + } + if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0) + { + log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE); } else { @@ -560,7 +577,7 @@ int net_server(const char *hostaddr, in_port_t port[]) if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 || ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 || - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-ed25519") < 0 || + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-rsa") < 0 || ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0) { log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind)); @@ -638,6 +655,8 @@ int net_server(const char *hostaddr, in_port_t port[]) fcntl(socket_server[i], F_SETFL, flags_server[i] | O_NONBLOCK); } + ssh_bind_set_blocking(sshbind, 0); + hash_dict_pid_sockaddr = hash_dict_create(MAX_CLIENT_LIMIT); if (hash_dict_pid_sockaddr == NULL) { @@ -791,6 +810,16 @@ int net_server(const char *hostaddr, in_port_t port[]) log_error("Reload BWF conf failed\n"); } + // Get EULA modification tm + if (stat(DATA_EULA, &file_stat) == -1) + { + log_error("stat(%s) error\n", DATA_EULA, errno); + } + else + { + BBS_eula_tm = file_stat.st_mtim.tv_sec; + } + if (detach_menu_shm(&bbs_menu) < 0) { log_error("detach_menu_shm(bbs_menu) error\n"); @@ -810,6 +839,7 @@ int net_server(const char *hostaddr, in_port_t port[]) log_error("load_menu(top10_menu) error\n"); unload_menu(&top10_menu); } + top10_menu.allow_exit = 1; for (int i = 0; i < data_files_load_startup_count; i++) { diff --git a/src/screen.c b/src/screen.c index e7fda9bd..7d5774be 100644 --- a/src/screen.c +++ b/src/screen.c @@ -88,7 +88,7 @@ void clrtobot(int line_begin) void clearscr() { prints("\033[2J"); - moveto(0, 0); + moveto(1, 1); } inline int press_any_key() diff --git a/src/section_list_display.c b/src/section_list_display.c index ddab0b12..5ca6ec3a 100644 --- a/src/section_list_display.c +++ b/src/section_list_display.c @@ -270,6 +270,12 @@ static enum select_cmd_t section_list_select(int total_page, int item_count, int if (ch != KEY_NULL && ch != KEY_TIMEOUT) { BBS_last_access_tm = time(NULL); + + // Refresh current action + if (user_online_update(NULL) < 0) + { + log_error("user_online_update(NULL) error\n"); + } } switch (ch) @@ -1398,6 +1404,12 @@ int section_list_ex_dir_display(SECTION_LIST *p_section) if (ch != KEY_NULL && ch != KEY_TIMEOUT) { BBS_last_access_tm = time(NULL); + + // Refresh current action + if (user_online_update(NULL) < 0) + { + log_error("user_online_update(NULL) error\n"); + } } switch (ch) diff --git a/src/test_ssh_server.c b/src/test_ssh_server.c index 2d8d3531..7f9d8523 100644 --- a/src/test_ssh_server.c +++ b/src/test_ssh_server.c @@ -25,6 +25,7 @@ enum test_ssh_server_constant_t static const char SSH_HOST_RSA_KEY_FILE[] = "../conf/ssh_host_rsa_key"; static const char SSH_HOST_ED25519_KEY_FILE[] = "../conf/ssh_host_ed25519_key"; +static const char SSH_HOST_ECDSA_KEY_FILE[] = "../conf/ssh_host_ecdsa_key"; static const char USER[] = "test"; static const char PASSWORD[] = "123456"; @@ -127,7 +128,7 @@ int ssh_server(const char *hostaddr, unsigned int port) if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_RSA_KEY_FILE) < 0) { - log_error("Error setting SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE); + log_error("Error loading SSH RSA key: %s\n", SSH_HOST_RSA_KEY_FILE); } else { @@ -135,7 +136,15 @@ int ssh_server(const char *hostaddr, unsigned int port) } if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ED25519_KEY_FILE) < 0) { - log_error("Error setting SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE); + log_error("Error loading SSH ED25519 key: %s\n", SSH_HOST_ED25519_KEY_FILE); + } + else + { + ssh_key_valid = 1; + } + if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, SSH_HOST_ECDSA_KEY_FILE) < 0) + { + log_error("Error loading SSH ECDSA key: %s\n", SSH_HOST_ECDSA_KEY_FILE); } else { @@ -151,7 +160,7 @@ int ssh_server(const char *hostaddr, unsigned int port) if (ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, hostaddr) < 0 || ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT, &port) < 0 || - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-ed25519") < 0 || + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, "+ssh-rsa") < 0 || ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level) < 0) { log_error("Error setting SSH bind options: %s\n", ssh_get_error(sshbind)); diff --git a/src/user_list.c b/src/user_list.c index abc72bab..0cd866e9 100644 --- a/src/user_list.c +++ b/src/user_list.c @@ -91,6 +91,7 @@ const USER_ACTION_MAP user_action_map[] = {"MENU", "菜单选择"}, {"POST_ARTICLE", "撰写文章"}, {"REPLY_ARTICLE", "回复文章"}, + {"TOP10_MENU", "十大热门"}, {"USER_LIST", "查花名册"}, {"USER_ONLINE", "环顾四周"}, {"VIEW_ARTICLE", "阅读文章"}, diff --git a/src/user_list_display.c b/src/user_list_display.c index 299aa071..2bb15d59 100644 --- a/src/user_list_display.c +++ b/src/user_list_display.c @@ -218,6 +218,12 @@ static enum select_cmd_t user_list_select(int total_page, int item_count, int *p if (ch != KEY_NULL && ch != KEY_TIMEOUT) { BBS_last_access_tm = time(NULL); + + // Refresh current action + if (user_online_update(NULL) < 0) + { + log_error("user_online_update(NULL) error\n"); + } } switch (ch)