--- lbbs/src/article_post.c 2025/10/10 03:50:22 1.29 +++ lbbs/src/article_post.c 2025/11/10 14:22:18 1.42 @@ -1,22 +1,15 @@ -/*************************************************************************** - article_post.c - description - ------------------- - copyright : (C) 2004-2025 by Leaflet - email : leaflet@leafok.com - ***************************************************************************/ - -/*************************************************************************** - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - ***************************************************************************/ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * article_post + * - user interactive feature to post / modify / reply article + * + * Copyright (C) 2004-2025 Leaflet + */ #include "article_cache.h" #include "article_post.h" #include "bbs.h" +#include "bwf.h" #include "database.h" #include "editor.h" #include "io.h" @@ -29,12 +22,12 @@ #include #include -#define TITLE_INPUT_MAX_LEN 72 -#define ARTICLE_CONTENT_MAX_LEN 1024 * 1024 * 4 // 4MB -#define ARTICLE_QUOTE_MAX_LINES 20 -#define ARTICLE_QUOTE_LINE_MAX_LEN 76 - -#define MODIFY_DT_MAX_LEN 50 +enum _article_post_constant_t +{ + TITLE_INPUT_MAX_LEN = 72, + ARTICLE_QUOTE_DEFAULT_LINES = 20, + MODIFY_DT_MAX_LEN = 50, +}; int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new) { @@ -90,7 +83,7 @@ int article_post(const SECTION_LIST *p_s { clearscr(); moveto(21, 1); - prints("发表文章于 %s[%s] 讨论区,类型: %s,通知:%s", + prints("发表文章于 %s[%s] 讨论区,类型: %s,回复通知: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"), (reply_note ? "开启" : "关闭")); @@ -106,12 +99,12 @@ int article_post(const SECTION_LIST *p_s moveto(24, 1); prints("T改标题, C取消, Z设为%s, N%s, Enter继续: ", (p_article_new->transship ? "原创" : "转载"), - (reply_note ? "通知关闭" : "通知开启")); + (reply_note ? "关闭回复通知" : "开启回复通知")); iflush(); ch = 0; } - for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME)) + while (!SYS_server_exit) { switch (toupper(ch)) { @@ -119,7 +112,6 @@ int article_post(const SECTION_LIST *p_s case KEY_TIMEOUT: goto cleanup; case CR: - igetch_reset(); break; case 'T': len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN); @@ -131,6 +123,15 @@ int article_post(const SECTION_LIST *p_s len = q - p; if (*p != '\0') { + if ((ret = check_badwords(p, '*')) < 0) + { + log_error("check_badwords(title) error\n"); + } + else if (ret > 0) + { + memcpy(title_input, p, (size_t)len + 1); + continue; + } memcpy(p_article_new->title, p, (size_t)len + 1); memcpy(title_input, p_article_new->title, (size_t)len + 1); } @@ -155,6 +156,7 @@ int article_post(const SECTION_LIST *p_s sign_id = ch - '0'; break; default: // Invalid selection + ch = igetch_t(BBS_max_user_idle_time); continue; } @@ -175,7 +177,7 @@ int article_post(const SECTION_LIST *p_s prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: "); iflush(); - for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME)) + for (ch = 0; !SYS_server_exit; ch = igetch_t(BBS_max_user_idle_time)) { switch (toupper(ch)) { @@ -183,7 +185,6 @@ int article_post(const SECTION_LIST *p_s case KEY_TIMEOUT: goto cleanup; case CR: - igetch_reset(); case 'S': break; case 'C': @@ -231,6 +232,13 @@ int article_post(const SECTION_LIST *p_s goto cleanup; } + if (check_badwords(content, '*') < 0) + { + log_error("check_badwords(content) error\n"); + ret = -1; + goto cleanup; + } + db = db_open(); if (db == NULL) { @@ -523,18 +531,17 @@ int article_modify(const SECTION_LIST *p { clearscr(); moveto(1, 1); - prints("(S)保存, (C)取消, (N)通知%s or (E)再编辑? [S]: ", + prints("(S)保存, (C)取消, (N)%s回复通知 or (E)再编辑? [S]: ", (reply_note ? "关闭" : "开启")); iflush(); - ch = igetch_t(MAX_DELAY_TIME); + ch = igetch_t(BBS_max_user_idle_time); switch (toupper(ch)) { case KEY_NULL: case KEY_TIMEOUT: goto cleanup; case CR: - igetch_reset(); case 'S': break; case 'C': @@ -578,6 +585,13 @@ int article_modify(const SECTION_LIST *p goto cleanup; } + if (check_badwords(content, '*') < 0) + { + log_error("check_badwords(content) error\n"); + ret = -1; + goto cleanup; + } + time(&now); localtime_r(&now, &tm_modify_dt); strftime(str_modify_dt, sizeof(str_modify_dt), "%Y-%m-%d %H:%M:%S (UTC %z)", &tm_modify_dt); @@ -722,7 +736,7 @@ int article_reply(const SECTION_LIST *p_ MYSQL *db = NULL; MYSQL_RES *rs = NULL; MYSQL_ROW row; - long line_offsets[ARTICLE_QUOTE_MAX_LINES + 1]; + long line_offsets[MAX_EDITOR_DATA_LINES + 1]; char sql[SQL_BUFFER_LEN]; char *sql_content = NULL; EDITOR_DATA *p_editor_data = NULL; @@ -734,7 +748,8 @@ int article_reply(const SECTION_LIST *p_ int content_display_length; char nickname_f[BBS_nickname_max_len * 2 + 1]; int sign_id = 0; - int reply_note = 1; + int reply_note = 0; + int full_quote = 0; long len; int ch; char *p, *q; @@ -854,49 +869,11 @@ int article_reply(const SECTION_LIST *p_ } // Apply LML render to content body - len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 0); + len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, MAX_EDITOR_DATA_LINE_LENGTH - 3, 1); content_f[len] = '\0'; // Remove control sequence len = str_filter(content_f, 0); - - len = snprintf(content, ARTICLE_CONTENT_MAX_LEN, - "\n\n【 在 %s (%s) 的大作中提到: 】\n", - p_article->username, p_article->nickname); - - quote_content_lines = split_data_lines(content_f, ARTICLE_QUOTE_LINE_MAX_LEN, line_offsets, ARTICLE_QUOTE_MAX_LINES + 1, 0, NULL); - for (i = 0; i < quote_content_lines; i++) - { - memcpy(content + len, ": ", 2); // quote line prefix - len += 2; - memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i])); - len += (line_offsets[i + 1] - line_offsets[i]); - if (content[len - 1] != '\n') // Appennd \n if not exist - { - content[len] = '\n'; - len++; - } - } - if (content[len - 1] != '\n') // Appennd \n if not exist - { - content[len] = '\n'; - len++; - } - content[len] = '\0'; - - free(content_f); - content_f = NULL; - - p_editor_data = editor_data_load(content); - if (p_editor_data == NULL) - { - log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0])); - ret = -1; - goto cleanup; - } - - free(content); - content = NULL; } mysql_free_result(rs); rs = NULL; @@ -909,7 +886,10 @@ int article_reply(const SECTION_LIST *p_ { clearscr(); moveto(21, 1); - prints("回复文章于 %s[%s] 讨论区,通知:%s", p_section->stitle, p_section->sname, (reply_note ? "开启" : "关闭")); + prints("回复文章于 %s[%s] 讨论区, 回复通知: %s, 引用模式: %s", + p_section->stitle, p_section->sname, + (reply_note ? "开启" : "关闭"), + (full_quote ? "完整" : "精简")); moveto(22, 1); prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title)); moveto(23, 1); @@ -920,13 +900,13 @@ int article_reply(const SECTION_LIST *p_ prints(" 按0~3选签名档(0表示不使用)"); moveto(24, 1); - prints("T改标题, C取消, N%s, Enter继续: ", - (reply_note ? "通知关闭" : "通知开启")); + prints("T改标题, C取消, N%s, Q%s, Enter继续: ", + (reply_note ? "关闭回复通知" : "开启回复通知"), (full_quote ? "精简引用" : "完整引用")); iflush(); ch = 0; } - for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME)) + while (!SYS_server_exit) { switch (toupper(ch)) { @@ -934,7 +914,6 @@ int article_reply(const SECTION_LIST *p_ case KEY_TIMEOUT: goto cleanup; case CR: - igetch_reset(); break; case 'T': len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN); @@ -946,6 +925,15 @@ int article_reply(const SECTION_LIST *p_ len = q - p; if (*p != '\0') { + if ((ret = check_badwords(p, '*')) < 0) + { + log_error("check_badwords(title) error\n"); + } + else if (ret > 0) + { + memcpy(title_input, p, (size_t)len + 1); + continue; + } memcpy(p_article_new->title, p, (size_t)len + 1); memcpy(title_input, p_article_new->title, (size_t)len + 1); } @@ -960,6 +948,9 @@ int article_reply(const SECTION_LIST *p_ case 'N': reply_note = (reply_note ? 0 : 1); break; + case 'Q': + full_quote = (full_quote ? 0 : 1); + break; case '0': case '1': case '2': @@ -967,6 +958,7 @@ int article_reply(const SECTION_LIST *p_ sign_id = ch - '0'; break; default: // Invalid selection + ch = igetch_t(BBS_max_user_idle_time); continue; } @@ -978,6 +970,47 @@ int article_reply(const SECTION_LIST *p_ continue; } + len = snprintf(content, ARTICLE_CONTENT_MAX_LEN, + "\n\n【 在 %s (%s) 的大作中提到: 】\n", + p_article->username, p_article->nickname); + + quote_content_lines = split_data_lines(content_f, + MAX_EDITOR_DATA_LINE_LENGTH - 2, line_offsets, + (full_quote ? MAX_EDITOR_DATA_LINES : ARTICLE_QUOTE_DEFAULT_LINES) + 1, + 0, NULL); + for (i = 0; i < quote_content_lines; i++) + { + memcpy(content + len, ": ", 2); // quote line prefix + len += 2; + memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i])); + len += (line_offsets[i + 1] - line_offsets[i]); + if (content[len - 1] != '\n') // Appennd \n if not exist + { + content[len] = '\n'; + len++; + } + } + if (content[len - 1] != '\n') // Appennd \n if not exist + { + content[len] = '\n'; + len++; + } + content[len] = '\0'; + + free(content_f); + content_f = NULL; + + p_editor_data = editor_data_load(content); + if (p_editor_data == NULL) + { + log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0])); + ret = -1; + goto cleanup; + } + + free(content); + content = NULL; + for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';) { editor_display(p_editor_data); @@ -987,7 +1020,7 @@ int article_reply(const SECTION_LIST *p_ prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: "); iflush(); - for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME)) + for (ch = 0; !SYS_server_exit; ch = igetch_t(BBS_max_user_idle_time)) { switch (toupper(ch)) { @@ -995,7 +1028,6 @@ int article_reply(const SECTION_LIST *p_ case KEY_TIMEOUT: goto cleanup; case CR: - igetch_reset(); case 'S': break; case 'C': @@ -1043,6 +1075,13 @@ int article_reply(const SECTION_LIST *p_ goto cleanup; } + if (check_badwords(content, '*') < 0) + { + log_error("check_badwords(content) error\n"); + ret = -1; + goto cleanup; + } + db = db_open(); if (db == NULL) { @@ -1211,7 +1250,7 @@ int article_reply(const SECTION_LIST *p_ { // Send notification message len_msg = snprintf(msg, BBS_msg_max_len, - "[hide]SYS_Reply_Article[/hide]有人回复了您所发表/回复的文章,快来" + "有人回复了您所发表/回复的文章,快来" "[article %d]看看[/article]《%s》吧!\n", p_article_new->aid, title_f);