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

Diff of /lbbs/src/article_post.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.34 by sysadm, Sat Oct 18 13:22:52 2025 UTC Revision 1.42 by sysadm, Mon Nov 10 14:22:18 2025 UTC
# Line 1  Line 1 
1  /***************************************************************************  /* SPDX-License-Identifier: GPL-3.0-or-later */
2                                                  article_post.c  -  description  /*
3                                                           -------------------   * article_post
4          copyright            : (C) 2004-2025 by Leaflet   *   - user interactive feature to post / modify / reply article
5          email                : leaflet@leafok.com   *
6   ***************************************************************************/   * Copyright (C) 2004-2025  Leaflet <leaflet@leafok.com>
7     */
 /***************************************************************************  
  *                                                                         *  
  *   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.                                   *  
  *                                                                         *  
  ***************************************************************************/  
8    
9  #include "article_cache.h"  #include "article_cache.h"
10  #include "article_post.h"  #include "article_post.h"
11  #include "bbs.h"  #include "bbs.h"
12    #include "bwf.h"
13  #include "database.h"  #include "database.h"
14  #include "editor.h"  #include "editor.h"
15  #include "io.h"  #include "io.h"
# Line 29  Line 22 
22  #include <stdlib.h>  #include <stdlib.h>
23  #include <time.h>  #include <time.h>
24    
25  #define TITLE_INPUT_MAX_LEN 72  enum _article_post_constant_t
26  #define ARTICLE_CONTENT_MAX_LEN 1024 * 1024 * 4 // 4MB  {
27  #define ARTICLE_QUOTE_MAX_LINES 20          TITLE_INPUT_MAX_LEN = 72,
28  #define ARTICLE_QUOTE_LINE_MAX_LEN 76          ARTICLE_QUOTE_DEFAULT_LINES = 20,
29            MODIFY_DT_MAX_LEN = 50,
30  #define MODIFY_DT_MAX_LEN 50  };
31    
32  int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new)  int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new)
33  {  {
# Line 90  int article_post(const SECTION_LIST *p_s Line 83  int article_post(const SECTION_LIST *p_s
83          {          {
84                  clearscr();                  clearscr();
85                  moveto(21, 1);                  moveto(21, 1);
86                  prints("发表文章于 %s[%s] 讨论区,类型: %s,回复通知:%s",                  prints("发表文章于 %s[%s] 讨论区,类型: %s,回复通知: %s",
87                             p_section->stitle, p_section->sname,                             p_section->stitle, p_section->sname,
88                             (p_article_new->transship ? "转载" : "原创"),                             (p_article_new->transship ? "转载" : "原创"),
89                             (reply_note ? "开启" : "关闭"));                             (reply_note ? "开启" : "关闭"));
# Line 111  int article_post(const SECTION_LIST *p_s Line 104  int article_post(const SECTION_LIST *p_s
104                          ch = 0;                          ch = 0;
105                  }                  }
106    
107                  for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                  while (!SYS_server_exit)
108                  {                  {
109                          switch (toupper(ch))                          switch (toupper(ch))
110                          {                          {
# Line 130  int article_post(const SECTION_LIST *p_s Line 123  int article_post(const SECTION_LIST *p_s
123                                  len = q - p;                                  len = q - p;
124                                  if (*p != '\0')                                  if (*p != '\0')
125                                  {                                  {
126                                            if ((ret = check_badwords(p, '*')) < 0)
127                                            {
128                                                    log_error("check_badwords(title) error\n");
129                                            }
130                                            else if (ret > 0)
131                                            {
132                                                    memcpy(title_input, p, (size_t)len + 1);
133                                                    continue;
134                                            }
135                                          memcpy(p_article_new->title, p, (size_t)len + 1);                                          memcpy(p_article_new->title, p, (size_t)len + 1);
136                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);
137                                  }                                  }
# Line 154  int article_post(const SECTION_LIST *p_s Line 156  int article_post(const SECTION_LIST *p_s
156                                  sign_id = ch - '0';                                  sign_id = ch - '0';
157                                  break;                                  break;
158                          default: // Invalid selection                          default: // Invalid selection
159                                    ch = igetch_t(BBS_max_user_idle_time);
160                                  continue;                                  continue;
161                          }                          }
162    
# Line 174  int article_post(const SECTION_LIST *p_s Line 177  int article_post(const SECTION_LIST *p_s
177                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
178                          iflush();                          iflush();
179    
180                          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))
181                          {                          {
182                                  switch (toupper(ch))                                  switch (toupper(ch))
183                                  {                                  {
# Line 229  int article_post(const SECTION_LIST *p_s Line 232  int article_post(const SECTION_LIST *p_s
232                  goto cleanup;                  goto cleanup;
233          }          }
234    
235            if (check_badwords(content, '*') < 0)
236            {
237                    log_error("check_badwords(content) error\n");
238                    ret = -1;
239                    goto cleanup;
240            }
241    
242          db = db_open();          db = db_open();
243          if (db == NULL)          if (db == NULL)
244          {          {
# Line 525  int article_modify(const SECTION_LIST *p Line 535  int article_modify(const SECTION_LIST *p
535                                     (reply_note ? "关闭" : "开启"));                                     (reply_note ? "关闭" : "开启"));
536                          iflush();                          iflush();
537    
538                          ch = igetch_t(MAX_DELAY_TIME);                          ch = igetch_t(BBS_max_user_idle_time);
539                          switch (toupper(ch))                          switch (toupper(ch))
540                          {                          {
541                          case KEY_NULL:                          case KEY_NULL:
# Line 575  int article_modify(const SECTION_LIST *p Line 585  int article_modify(const SECTION_LIST *p
585                  goto cleanup;                  goto cleanup;
586          }          }
587    
588            if (check_badwords(content, '*') < 0)
589            {
590                    log_error("check_badwords(content) error\n");
591                    ret = -1;
592                    goto cleanup;
593            }
594    
595          time(&now);          time(&now);
596          localtime_r(&now, &tm_modify_dt);          localtime_r(&now, &tm_modify_dt);
597          strftime(str_modify_dt, sizeof(str_modify_dt), "%Y-%m-%d %H:%M:%S (UTC %z)", &tm_modify_dt);          strftime(str_modify_dt, sizeof(str_modify_dt), "%Y-%m-%d %H:%M:%S (UTC %z)", &tm_modify_dt);
# Line 719  int article_reply(const SECTION_LIST *p_ Line 736  int article_reply(const SECTION_LIST *p_
736          MYSQL *db = NULL;          MYSQL *db = NULL;
737          MYSQL_RES *rs = NULL;          MYSQL_RES *rs = NULL;
738          MYSQL_ROW row;          MYSQL_ROW row;
739          long line_offsets[ARTICLE_QUOTE_MAX_LINES + 1];          long line_offsets[MAX_EDITOR_DATA_LINES + 1];
740          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
741          char *sql_content = NULL;          char *sql_content = NULL;
742          EDITOR_DATA *p_editor_data = NULL;          EDITOR_DATA *p_editor_data = NULL;
# Line 732  int article_reply(const SECTION_LIST *p_ Line 749  int article_reply(const SECTION_LIST *p_
749          char nickname_f[BBS_nickname_max_len * 2 + 1];          char nickname_f[BBS_nickname_max_len * 2 + 1];
750          int sign_id = 0;          int sign_id = 0;
751          int reply_note = 0;          int reply_note = 0;
752            int full_quote = 0;
753          long len;          long len;
754          int ch;          int ch;
755          char *p, *q;          char *p, *q;
# Line 851  int article_reply(const SECTION_LIST *p_ Line 869  int article_reply(const SECTION_LIST *p_
869                  }                  }
870    
871                  // Apply LML render to content body                  // Apply LML render to content body
872                  len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 1);                  len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, MAX_EDITOR_DATA_LINE_LENGTH - 3, 1);
873                  content_f[len] = '\0';                  content_f[len] = '\0';
874    
875                  // Remove control sequence                  // Remove control sequence
876                  len = str_filter(content_f, 0);                  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;  
877          }          }
878          mysql_free_result(rs);          mysql_free_result(rs);
879          rs = NULL;          rs = NULL;
# Line 906  int article_reply(const SECTION_LIST *p_ Line 886  int article_reply(const SECTION_LIST *p_
886          {          {
887                  clearscr();                  clearscr();
888                  moveto(21, 1);                  moveto(21, 1);
889                  prints("回复文章于 %s[%s] 讨论区,回复通知:%s", p_section->stitle, p_section->sname, (reply_note ? "开启" : "关闭"));                  prints("回复文章于 %s[%s] 讨论区, 回复通知: %s, 引用模式: %s",
890                               p_section->stitle, p_section->sname,
891                               (reply_note ? "开启" : "关闭"),
892                               (full_quote ? "完整" : "精简"));
893                  moveto(22, 1);                  moveto(22, 1);
894                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
895                  moveto(23, 1);                  moveto(23, 1);
# Line 917  int article_reply(const SECTION_LIST *p_ Line 900  int article_reply(const SECTION_LIST *p_
900                          prints("    按0~3选签名档(0表示不使用)");                          prints("    按0~3选签名档(0表示不使用)");
901    
902                          moveto(24, 1);                          moveto(24, 1);
903                          prints("T改标题, C取消, N%s, Enter继续: ",                          prints("T改标题, C取消, N%s, Q%s, Enter继续: ",
904                                     (reply_note ? "关闭回复通知" : "开启回复通知"));                                     (reply_note ? "关闭回复通知" : "开启回复通知"), (full_quote ? "精简引用" : "完整引用"));
905                          iflush();                          iflush();
906                          ch = 0;                          ch = 0;
907                  }                  }
908    
909                  for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                  while (!SYS_server_exit)
910                  {                  {
911                          switch (toupper(ch))                          switch (toupper(ch))
912                          {                          {
# Line 942  int article_reply(const SECTION_LIST *p_ Line 925  int article_reply(const SECTION_LIST *p_
925                                  len = q - p;                                  len = q - p;
926                                  if (*p != '\0')                                  if (*p != '\0')
927                                  {                                  {
928                                            if ((ret = check_badwords(p, '*')) < 0)
929                                            {
930                                                    log_error("check_badwords(title) error\n");
931                                            }
932                                            else if (ret > 0)
933                                            {
934                                                    memcpy(title_input, p, (size_t)len + 1);
935                                                    continue;
936                                            }
937                                          memcpy(p_article_new->title, p, (size_t)len + 1);                                          memcpy(p_article_new->title, p, (size_t)len + 1);
938                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);
939                                  }                                  }
# Line 956  int article_reply(const SECTION_LIST *p_ Line 948  int article_reply(const SECTION_LIST *p_
948                          case 'N':                          case 'N':
949                                  reply_note = (reply_note ? 0 : 1);                                  reply_note = (reply_note ? 0 : 1);
950                                  break;                                  break;
951                            case 'Q':
952                                    full_quote = (full_quote ? 0 : 1);
953                                    break;
954                          case '0':                          case '0':
955                          case '1':                          case '1':
956                          case '2':                          case '2':
# Line 963  int article_reply(const SECTION_LIST *p_ Line 958  int article_reply(const SECTION_LIST *p_
958                                  sign_id = ch - '0';                                  sign_id = ch - '0';
959                                  break;                                  break;
960                          default: // Invalid selection                          default: // Invalid selection
961                                    ch = igetch_t(BBS_max_user_idle_time);
962                                  continue;                                  continue;
963                          }                          }
964    
# Line 974  int article_reply(const SECTION_LIST *p_ Line 970  int article_reply(const SECTION_LIST *p_
970                          continue;                          continue;
971                  }                  }
972    
973                    len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
974                                               "\n\n【 在 %s (%s) 的大作中提到: 】\n",
975                                               p_article->username, p_article->nickname);
976    
977                    quote_content_lines = split_data_lines(content_f,
978                                                                                               MAX_EDITOR_DATA_LINE_LENGTH - 2, line_offsets,
979                                                                                               (full_quote ? MAX_EDITOR_DATA_LINES : ARTICLE_QUOTE_DEFAULT_LINES) + 1,
980                                                                                               0, NULL);
981                    for (i = 0; i < quote_content_lines; i++)
982                    {
983                            memcpy(content + len, ": ", 2); // quote line prefix
984                            len += 2;
985                            memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i]));
986                            len += (line_offsets[i + 1] - line_offsets[i]);
987                            if (content[len - 1] != '\n') // Appennd \n if not exist
988                            {
989                                    content[len] = '\n';
990                                    len++;
991                            }
992                    }
993                    if (content[len - 1] != '\n') // Appennd \n if not exist
994                    {
995                            content[len] = '\n';
996                            len++;
997                    }
998                    content[len] = '\0';
999    
1000                    free(content_f);
1001                    content_f = NULL;
1002    
1003                    p_editor_data = editor_data_load(content);
1004                    if (p_editor_data == NULL)
1005                    {
1006                            log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
1007                            ret = -1;
1008                            goto cleanup;
1009                    }
1010    
1011                    free(content);
1012                    content = NULL;
1013    
1014                  for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)                  for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
1015                  {                  {
1016                          editor_display(p_editor_data);                          editor_display(p_editor_data);
# Line 983  int article_reply(const SECTION_LIST *p_ Line 1020  int article_reply(const SECTION_LIST *p_
1020                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
1021                          iflush();                          iflush();
1022    
1023                          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))
1024                          {                          {
1025                                  switch (toupper(ch))                                  switch (toupper(ch))
1026                                  {                                  {
# Line 1037  int article_reply(const SECTION_LIST *p_ Line 1074  int article_reply(const SECTION_LIST *p_
1074                  ret = -1;                  ret = -1;
1075                  goto cleanup;                  goto cleanup;
1076          }          }
1077    
1078            if (check_badwords(content, '*') < 0)
1079            {
1080                    log_error("check_badwords(content) error\n");
1081                    ret = -1;
1082                    goto cleanup;
1083            }
1084    
1085          db = db_open();          db = db_open();
1086          if (db == NULL)          if (db == NULL)


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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