/[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.12 by sysadm, Sat Jun 14 12:30:15 2025 UTC Revision 1.33 by sysadm, Fri Oct 17 01:25:08 2025 UTC
# Line 14  Line 14 
14   *                                                                         *   *                                                                         *
15   ***************************************************************************/   ***************************************************************************/
16    
 #define _POSIX_C_SOURCE 200809L  
   
 #include "article_post.h"  
17  #include "article_cache.h"  #include "article_cache.h"
18  #include "editor.h"  #include "article_post.h"
 #include "screen.h"  
19  #include "bbs.h"  #include "bbs.h"
20  #include "log.h"  #include "database.h"
21    #include "editor.h"
22  #include "io.h"  #include "io.h"
23    #include "log.h"
24  #include "lml.h"  #include "lml.h"
25  #include "database.h"  #include "screen.h"
26  #include "user_priv.h"  #include "user_priv.h"
27  #include <ctype.h>  #include <ctype.h>
28  #include <string.h>  #include <string.h>
29  #include <stdlib.h>  #include <stdlib.h>
30  #include <time.h>  #include <time.h>
31    
32  #define TITLE_INPUT_MAX_LEN 74  #define TITLE_INPUT_MAX_LEN 72
33  #define ARTICLE_CONTENT_MAX_LEN 1024 * 1024 * 4 // 4MB  #define ARTICLE_CONTENT_MAX_LEN 1024 * 1024 * 4 // 4MB
34  #define ARTICLE_QUOTE_MAX_LINES 20  #define ARTICLE_QUOTE_MAX_LINES 20
35  #define ARTICLE_QUOTE_LINE_MAX_LEN 76  #define ARTICLE_QUOTE_LINE_MAX_LEN 76
# Line 46  int article_post(const SECTION_LIST *p_s Line 44  int article_post(const SECTION_LIST *p_s
44          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
45          char *sql_content = NULL;          char *sql_content = NULL;
46          EDITOR_DATA *p_editor_data = NULL;          EDITOR_DATA *p_editor_data = NULL;
47          char title_input[TITLE_INPUT_MAX_LEN + 1];          char title_input[BBS_article_title_max_len + 1];
48          char title_f[BBS_article_title_max_len * 2 + 1];          char title_f[BBS_article_title_max_len * 2 + 1];
49          char *content = NULL;          char *content = NULL;
50          char *content_f = NULL;          char *content_f = NULL;
51          long len_content;          long len_content;
52            int content_display_length;
53          char nickname_f[BBS_nickname_max_len * 2 + 1];          char nickname_f[BBS_nickname_max_len * 2 + 1];
54          int sign_id = 0;          int sign_id = 0;
55            int reply_note = 1;
56          long len;          long len;
57          int ch;          int ch;
58          char *p, *q;          char *p, *q;
# Line 67  int article_post(const SECTION_LIST *p_s Line 67  int article_post(const SECTION_LIST *p_s
67          {          {
68                  clearscr();                  clearscr();
69                  moveto(1, 1);                  moveto(1, 1);
70                  prints("您没有权限在本版块发表文章\n");                  prints("鎮ㄦ病鏈夋潈闄愬湪鏈増鍧楀彂琛ㄦ枃绔燶n");
71                  press_any_key();                  press_any_key();
72    
73                  return 0;                  return 0;
# Line 81  int article_post(const SECTION_LIST *p_s Line 81  int article_post(const SECTION_LIST *p_s
81          if (p_editor_data == NULL)          if (p_editor_data == NULL)
82          {          {
83                  log_error("editor_data_load() error\n");                  log_error("editor_data_load() error\n");
84                  return -2;                  ret = -1;
85                    goto cleanup;
86          }          }
87    
88          // Set title and sign          // Set title and sign
# Line 89  int article_post(const SECTION_LIST *p_s Line 90  int article_post(const SECTION_LIST *p_s
90          {          {
91                  clearscr();                  clearscr();
92                  moveto(21, 1);                  moveto(21, 1);
93                  prints("发表文章于 %s[%s] 讨论区,类型: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"));                  prints("鍙戣〃鏂囩珷浜 %s[%s] 璁ㄨ鍖猴紝绫诲瀷: %s锛屽洖澶嶉氱煡锛%s",
94                               p_section->stitle, p_section->sname,
95                               (p_article_new->transship ? "杞浇" : "鍘熷垱"),
96                               (reply_note ? "寮鍚" : "鍏抽棴"));
97                  moveto(22, 1);                  moveto(22, 1);
98                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));                  prints("鏍囬: %s", (p_article_new->title[0] == '\0' ? "[鏃燷" : p_article_new->title));
99                  moveto(23, 1);                  moveto(23, 1);
100                  prints("使用第 %d 个签名", sign_id);                  prints("浣跨敤绗 %d 涓鍚", sign_id);
101    
102                  if (toupper(ch) != 'T')                  if (toupper(ch) != 'T')
103                  {                  {
104                          prints("    按0~3选签名档(0表示不使用)");                          prints("    鎸0~3閫夌鍚嶆。(0琛ㄧず涓嶄娇鐢)");
105    
106                          moveto(24, 1);                          moveto(24, 1);
107                          prints("T改标题, C取消, Z设为转载, Y设为原创, Enter继续: ");                          prints("T鏀规爣棰, C鍙栨秷, Z璁句负%s, N%s, Enter缁х画: ",
108                                       (p_article_new->transship ? "鍘熷垱" : "杞浇"),
109                                       (reply_note ? "鍏抽棴鍥炲閫氱煡" : "寮鍚洖澶嶉氱煡"));
110                          iflush();                          iflush();
111                          ch = 0;                          ch = 0;
112                  }                  }
# Line 109  int article_post(const SECTION_LIST *p_s Line 115  int article_post(const SECTION_LIST *p_s
115                  {                  {
116                          switch (toupper(ch))                          switch (toupper(ch))
117                          {                          {
118                            case KEY_NULL:
119                            case KEY_TIMEOUT:
120                                    goto cleanup;
121                          case CR:                          case CR:
                                 igetch_reset();  
122                                  break;                                  break;
123                          case 'T':                          case 'T':
124                                  moveto(24, 1);                                  len = get_data(24, 1, "鏍囬: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
                                 clrtoeol();  
                                 len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);  
125                                  for (p = title_input; *p == ' '; p++)                                  for (p = title_input; *p == ' '; p++)
126                                          ;                                          ;
127                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
# Line 132  int article_post(const SECTION_LIST *p_s Line 138  int article_post(const SECTION_LIST *p_s
138                          case 'C':                          case 'C':
139                                  clearscr();                                  clearscr();
140                                  moveto(1, 1);                                  moveto(1, 1);
141                                  prints("取消...");                                  prints("鍙栨秷...");
142                                  press_any_key();                                  press_any_key();
143                                  goto cleanup;                                  goto cleanup;
                         case 'Y':  
                                 p_article_new->transship = 0;  
                                 break;  
144                          case 'Z':                          case 'Z':
145                                  p_article_new->transship = 1;                                  p_article_new->transship = (p_article_new->transship ? 0 : 1);
146                                    break;
147                            case 'N':
148                                    reply_note = (reply_note ? 0 : 1);
149                                  break;                                  break;
150                          case '0':                          case '0':
151                          case '1':                          case '1':
# Line 165  int article_post(const SECTION_LIST *p_s Line 171  int article_post(const SECTION_LIST *p_s
171    
172                          clearscr();                          clearscr();
173                          moveto(1, 1);                          moveto(1, 1);
174                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");                          prints("(S)鍙戦, (C)鍙栨秷, (T)鏇存敼鏍囬 or (E)鍐嶇紪杈? [S]: ");
175                          iflush();                          iflush();
176    
177                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
178                          {                          {
179                                  switch (toupper(ch))                                  switch (toupper(ch))
180                                  {                                  {
181                                    case KEY_NULL:
182                                    case KEY_TIMEOUT:
183                                            goto cleanup;
184                                  case CR:                                  case CR:
                                         igetch_reset();  
185                                  case 'S':                                  case 'S':
186                                          break;                                          break;
187                                  case 'C':                                  case 'C':
188                                          clearscr();                                          clearscr();
189                                          moveto(1, 1);                                          moveto(1, 1);
190                                          prints("取消...");                                          prints("鍙栨秷...");
191                                          press_any_key();                                          press_any_key();
192                                          goto cleanup;                                          goto cleanup;
193                                  case 'T':                                  case 'T':
# Line 200  int article_post(const SECTION_LIST *p_s Line 208  int article_post(const SECTION_LIST *p_s
208                  }                  }
209          }          }
210    
211            if (SYS_server_exit) // Do not save data on shutdown
212            {
213                    goto cleanup;
214            }
215    
216          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
217          if (content == NULL)          if (content == NULL)
218          {          {
# Line 212  int article_post(const SECTION_LIST *p_s Line 225  int article_post(const SECTION_LIST *p_s
225          if (len_content < 0)          if (len_content < 0)
226          {          {
227                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
228                  ret = -2;                  ret = -1;
229                  goto cleanup;                  goto cleanup;
230          }          }
231    
# Line 253  int article_post(const SECTION_LIST *p_s Line 266  int article_post(const SECTION_LIST *p_s
266                  rs = NULL;                  rs = NULL;
267          }          }
268    
269          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
270          if (content_f == NULL)          content_display_length = str_length(content, 1);
         {  
                 log_error("malloc(content_f) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
   
         sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);  
         if (sql_content == NULL)  
         {  
                 log_error("malloc(sql_content) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
271    
272          // Begin transaction          // Begin transaction
273          if (mysql_query(db, "SET autocommit=0") != 0)          if (mysql_query(db, "SET autocommit=0") != 0)
# Line 285  int article_post(const SECTION_LIST *p_s Line 285  int article_post(const SECTION_LIST *p_s
285          }          }
286    
287          // Secure SQL parameters          // Secure SQL parameters
288            content_f = malloc((size_t)len_content * 2 + 1);
289            if (content_f == NULL)
290            {
291                    log_error("malloc(content_f) error: OOM\n");
292                    ret = -1;
293                    goto cleanup;
294            }
295    
296          mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));          mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
297          mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));          mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
298          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
299    
300            free(content);
301            content = NULL;
302    
303          // Add content          // Add content
304          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
305            if (sql_content == NULL)
306            {
307                    log_error("malloc(sql_content) error: OOM\n");
308                    ret = -1;
309                    goto cleanup;
310            }
311    
312            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
313                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",
314                           content_f);                           content_f);
315    
316          if (mysql_query(db, sql) != 0)          free(content_f);
317            content_f = NULL;
318    
319            if (mysql_query(db, sql_content) != 0)
320          {          {
321                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
322                  ret = -1;                  ret = -1;
# Line 303  int article_post(const SECTION_LIST *p_s Line 325  int article_post(const SECTION_LIST *p_s
325    
326          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
327    
328            free(sql_content);
329            sql_content = NULL;
330    
331          // Add article          // Add article
332          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
333                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
334                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
335                           "VALUES(%d, 0, %d, '%s', '%s', '%s', %d, %d, NOW(), '%s', 1, %d, NOW(), 1, %ld)",                           "VALUES(%d, 0, %d, '%s', '%s', '%s', %d, %d, NOW(), '%s', %d, %d, NOW(), 1, %d)",
336                           p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,                           p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,
337                           p_article_new->cid, p_article_new->transship, hostaddr_client, BBS_user_exp, len_content);                           p_article_new->cid, p_article_new->transship, hostaddr_client,
338                             reply_note, BBS_user_exp, content_display_length);
339    
340          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
341          {          {
# Line 368  int article_post(const SECTION_LIST *p_s Line 394  int article_post(const SECTION_LIST *p_s
394                  goto cleanup;                  goto cleanup;
395          }          }
396    
397            mysql_close(db);
398            db = NULL;
399    
400          clearscr();          clearscr();
401          moveto(1, 1);          moveto(1, 1);
402          prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);          prints("鍙戦佸畬鎴愶紝鏂版枃绔犻氬父浼氬湪%d绉掑悗鍙", BBS_section_list_load_interval);
403          press_any_key();          press_any_key();
404          ret = 1; // Success          ret = 1; // Success
405    
# Line 397  int article_modify(const SECTION_LIST *p Line 426  int article_modify(const SECTION_LIST *p
426          char *content = NULL;          char *content = NULL;
427          char *content_f = NULL;          char *content_f = NULL;
428          long len_content;          long len_content;
429            int content_display_length;
430            int reply_note = 1;
431          int ch;          int ch;
432          long ret = 0;          long ret = 0;
433          time_t now;          time_t now;
# Line 414  int article_modify(const SECTION_LIST *p Line 445  int article_modify(const SECTION_LIST *p
445          {          {
446                  clearscr();                  clearscr();
447                  moveto(1, 1);                  moveto(1, 1);
448                  prints("该文章无法被编辑,请联系版主。");                  prints("璇ユ枃绔犳棤娉曡缂栬緫锛岃鑱旂郴鐗堜富銆");
                 press_any_key();  
   
                 return 0;  
         }  
   
         if (!checkpriv(&BBS_priv, p_section->sid, S_POST))  
         {  
                 clearscr();  
                 moveto(1, 1);  
                 prints("您没有权限在本版块发表文章\n");  
449                  press_any_key();                  press_any_key();
450    
451                  return 0;                  return 0;
# Line 434  int article_modify(const SECTION_LIST *p Line 455  int article_modify(const SECTION_LIST *p
455          if (db == NULL)          if (db == NULL)
456          {          {
457                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
458                  return -1;                  ret = -1;
459                    goto cleanup;
460          }          }
461    
462          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
463                           "SELECT bbs_content.CID, bbs_content.content "                           "SELECT bbs_content.CID, bbs_content.content, reply_note "
464                           "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "                           "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "
465                           "WHERE bbs.AID = %d",                           "WHERE bbs.AID = %d",
466                           p_article->aid);                           p_article->aid);
# Line 446  int article_modify(const SECTION_LIST *p Line 468  int article_modify(const SECTION_LIST *p
468          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
469          {          {
470                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
471                  ret = -2;                  ret = -1;
472                  goto cleanup;                  goto cleanup;
473          }          }
474          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
475          {          {
476                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
477                  ret = -2;                  ret = -1;
478                  goto cleanup;                  goto cleanup;
479          }          }
480    
481          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
482          {          {
483                  p_editor_data = editor_data_load(row[1]);                  content = malloc(ARTICLE_CONTENT_MAX_LEN);
484                    if (content == NULL)
485                    {
486                            log_error("malloc(content) error: OOM\n");
487                            ret = -1;
488                            goto cleanup;
489                    }
490    
491                    strncpy(content, row[1], ARTICLE_CONTENT_MAX_LEN - 1);
492                    content[ARTICLE_CONTENT_MAX_LEN - 1] = '\0';
493    
494                    // Remove control sequence
495                    len_content = str_filter(content, 0);
496    
497                    p_editor_data = editor_data_load(content);
498                  if (p_editor_data == NULL)                  if (p_editor_data == NULL)
499                  {                  {
500                          log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));                          log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
501                          ret = -3;                          ret = -1;
502                          goto cleanup;                          goto cleanup;
503                  }                  }
504    
505                    free(content);
506                    content = NULL;
507    
508                    reply_note = atoi(row[2]);
509          }          }
510          mysql_free_result(rs);          mysql_free_result(rs);
511          rs = NULL;          rs = NULL;
# Line 476  int article_modify(const SECTION_LIST *p Line 517  int article_modify(const SECTION_LIST *p
517          {          {
518                  editor_display(p_editor_data);                  editor_display(p_editor_data);
519    
520                  clearscr();                  while (!SYS_server_exit)
                 moveto(1, 1);  
                 prints("(S)保存, (C)取消 or (E)再编辑? [S]: ");  
                 iflush();  
   
                 for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))  
521                  {                  {
522                            clearscr();
523                            moveto(1, 1);
524                            prints("(S)淇濆瓨, (C)鍙栨秷, (N)%s鍥炲閫氱煡 or (E)鍐嶇紪杈? [S]: ",
525                                       (reply_note ? "鍏抽棴" : "寮鍚"));
526                            iflush();
527    
528                            ch = igetch_t(MAX_DELAY_TIME);
529                          switch (toupper(ch))                          switch (toupper(ch))
530                          {                          {
531                            case KEY_NULL:
532                            case KEY_TIMEOUT:
533                                    goto cleanup;
534                          case CR:                          case CR:
                                 igetch_reset();  
535                          case 'S':                          case 'S':
536                                  break;                                  break;
537                          case 'C':                          case 'C':
538                                  clearscr();                                  clearscr();
539                                  moveto(1, 1);                                  moveto(1, 1);
540                                  prints("取消...");                                  prints("鍙栨秷...");
541                                  press_any_key();                                  press_any_key();
542                                  goto cleanup;                                  goto cleanup;
543                            case 'N':
544                                    reply_note = (reply_note ? 0 : 1);
545                                    continue;
546                          case 'E':                          case 'E':
547                                  break;                                  break;
548                          default: // Invalid selection                          default: // Invalid selection
# Line 505  int article_modify(const SECTION_LIST *p Line 553  int article_modify(const SECTION_LIST *p
553                  }                  }
554          }          }
555    
556            if (SYS_server_exit) // Do not save data on shutdown
557            {
558                    goto cleanup;
559            }
560    
561          // Allocate buffers in big size          // Allocate buffers in big size
562          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
563          if (content == NULL)          if (content == NULL)
# Line 518  int article_modify(const SECTION_LIST *p Line 571  int article_modify(const SECTION_LIST *p
571          if (len_content < 0)          if (len_content < 0)
572          {          {
573                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
574                  ret = -2;                  ret = -1;
575                  goto cleanup;                  goto cleanup;
576          }          }
577    
# Line 527  int article_modify(const SECTION_LIST *p Line 580  int article_modify(const SECTION_LIST *p
580          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);
581    
582          len_content += snprintf(content + len_content, LINE_BUFFER_LEN,          len_content += snprintf(content + len_content, LINE_BUFFER_LEN,
583                                                          "\n--\n※ 作者已于 %s 修改本文※\n",                                                          "\n--\n鈥 浣滆呭凡浜 %s 淇敼鏈枃鈥籠n",
584                                                          str_modify_dt);                                                          str_modify_dt);
585    
586          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
587          if (content_f == NULL)          content_display_length = str_length(content, 1);
         {  
                 log_error("malloc(content_f) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
   
         sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);  
         if (sql_content == NULL)  
         {  
                 log_error("malloc(sql_content) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
588    
589          db = db_open();          db = db_open();
590          if (db == NULL)          if (db == NULL)
# Line 570  int article_modify(const SECTION_LIST *p Line 610  int article_modify(const SECTION_LIST *p
610          }          }
611    
612          // Secure SQL parameters          // Secure SQL parameters
613            content_f = malloc((size_t)len_content * 2 + 1);
614            if (content_f == NULL)
615            {
616                    log_error("malloc(content_f) error: OOM\n");
617                    ret = -1;
618                    goto cleanup;
619            }
620    
621          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
622    
623            free(content);
624            content = NULL;
625    
626          // Add content          // Add content
627          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
628            if (sql_content == NULL)
629            {
630                    log_error("malloc(sql_content) error: OOM\n");
631                    ret = -1;
632                    goto cleanup;
633            }
634    
635            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
636                           "INSERT INTO bbs_content(AID, content) values(%d, '%s')",                           "INSERT INTO bbs_content(AID, content) values(%d, '%s')",
637                           p_article->aid, content_f);                           p_article->aid, content_f);
638    
639          if (mysql_query(db, sql) != 0)          free(content_f);
640            content_f = NULL;
641    
642            if (mysql_query(db, sql_content) != 0)
643          {          {
644                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
645                  ret = -1;                  ret = -1;
# Line 586  int article_modify(const SECTION_LIST *p Line 648  int article_modify(const SECTION_LIST *p
648    
649          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
650    
651            free(sql_content);
652            sql_content = NULL;
653    
654          // Update article          // Update article
655          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
656                           "UPDATE bbs SET CID = %d, length = %ld WHERE AID = %d",                           "UPDATE bbs SET CID = %d, length = %d, reply_note = %d, excerption = 0 WHERE AID = %d", // Set excerption = 0 explictly in case of rare condition
657                           p_article_new->cid, len_content, p_article->aid);                           p_article_new->cid, content_display_length, reply_note, p_article->aid);
658    
659          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
660          {          {
# Line 626  int article_modify(const SECTION_LIST *p Line 691  int article_modify(const SECTION_LIST *p
691                  goto cleanup;                  goto cleanup;
692          }          }
693    
694            mysql_close(db);
695            db = NULL;
696    
697          clearscr();          clearscr();
698          moveto(1, 1);          moveto(1, 1);
699          prints("修改完成,新内容通常会在%d秒后可见", BBS_section_list_load_interval);          prints("淇敼瀹屾垚锛屾柊鍐呭閫氬父浼氬湪%d绉掑悗鍙", BBS_section_list_load_interval);
700          press_any_key();          press_any_key();
701          ret = 1; // Success          ret = 1; // Success
702    
703  cleanup:  cleanup:
704            mysql_free_result(rs);
705          mysql_close(db);          mysql_close(db);
706    
707          // Cleanup buffers          // Cleanup buffers
# Line 659  int article_reply(const SECTION_LIST *p_ Line 728  int article_reply(const SECTION_LIST *p_
728          char *content = NULL;          char *content = NULL;
729          char *content_f = NULL;          char *content_f = NULL;
730          long len_content;          long len_content;
731            int content_display_length;
732          char nickname_f[BBS_nickname_max_len * 2 + 1];          char nickname_f[BBS_nickname_max_len * 2 + 1];
733          int sign_id = 0;          int sign_id = 0;
734            int reply_note = 0;
735          long len;          long len;
736          int ch;          int ch;
737          char *p, *q;          char *p, *q;
# Line 669  int article_reply(const SECTION_LIST *p_ Line 740  int article_reply(const SECTION_LIST *p_
740          long quote_content_lines;          long quote_content_lines;
741          long i;          long i;
742          long ret = 0;          long ret = 0;
743            int topic_locked = 0;
744            char msg[BBS_msg_max_len];
745            char msg_f[BBS_msg_max_len * 2 + 1];
746            int len_msg;
747    
748          if (p_section == NULL || p_article == NULL)          if (p_section == NULL || p_article == NULL)
749          {          {
# Line 679  int article_reply(const SECTION_LIST *p_ Line 754  int article_reply(const SECTION_LIST *p_
754          {          {
755                  clearscr();                  clearscr();
756                  moveto(1, 1);                  moveto(1, 1);
757                  prints("您没有权限在本版块发表文章\n");                  prints("鎮ㄦ病鏈夋潈闄愬湪鏈増鍧楀彂琛ㄦ枃绔燶n");
                 press_any_key();  
   
                 return 0;  
         }  
   
         if (p_article->lock) // Reply is not allowed  
         {  
                 clearscr();  
                 moveto(1, 1);  
                 prints("该文章谢绝回复");  
758                  press_any_key();                  press_any_key();
759    
760                  return 0;                  return 0;
# Line 697  int article_reply(const SECTION_LIST *p_ Line 762  int article_reply(const SECTION_LIST *p_
762    
763          p_article_new->title[0] = '\0';          p_article_new->title[0] = '\0';
764          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
765          len = split_line(title_input, TITLE_INPUT_MAX_LEN, &eol, &display_len);          len = split_line(title_input, TITLE_INPUT_MAX_LEN, &eol, &display_len, 0);
766          title_input[len] = '\0';          title_input[len] = '\0';
767    
768          db = db_open();          db = db_open();
769          if (db == NULL)          if (db == NULL)
770          {          {
771                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
772                  return -1;                  ret = -1;
773                    goto cleanup;
774            }
775    
776            snprintf(sql, sizeof(sql),
777                             "SELECT `lock` FROM bbs WHERE AID = %d",
778                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
779    
780            if (mysql_query(db, sql) != 0)
781            {
782                    log_error("Query article status error: %s\n", mysql_error(db));
783                    ret = -1;
784                    goto cleanup;
785            }
786            if ((rs = mysql_store_result(db)) == NULL)
787            {
788                    log_error("Get article status data failed\n");
789                    ret = -1;
790                    goto cleanup;
791            }
792    
793            if ((row = mysql_fetch_row(rs)))
794            {
795                    if (atoi(row[0]) != 0)
796                    {
797                            topic_locked = 1;
798                    }
799            }
800            mysql_free_result(rs);
801            rs = NULL;
802    
803            if (topic_locked) // Reply is not allowed
804            {
805                    mysql_close(db);
806                    db = NULL;
807    
808                    clearscr();
809                    moveto(1, 1);
810                    prints("璇ヤ富棰樿阿缁濆洖澶");
811                    press_any_key();
812    
813                    goto cleanup;
814          }          }
815    
816          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 716  int article_reply(const SECTION_LIST *p_ Line 822  int article_reply(const SECTION_LIST *p_
822          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
823          {          {
824                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
825                  return -2;                  ret = -1;
826                    goto cleanup;
827          }          }
828          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
829          {          {
830                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
831                  return -2;                  ret = -1;
832                    goto cleanup;
833          }          }
834    
835          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
# Line 743  int article_reply(const SECTION_LIST *p_ Line 851  int article_reply(const SECTION_LIST *p_
851                  }                  }
852    
853                  // Apply LML render to content body                  // Apply LML render to content body
854                  len = lml_plain(row[1], content_f, ARTICLE_CONTENT_MAX_LEN);                  len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 0);
855                  content_f[len] = '\0';                  content_f[len] = '\0';
856    
857                  // Remove control sequence                  // Remove control sequence
858                  len = ctrl_seq_filter(content_f);                  len = str_filter(content_f, 0);
859    
860                  len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,                  len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
861                                             "\n\n【 在 %s (%s) 的大作中提到: 】\n",                                             "\n\n銆 鍦 %s (%s) 鐨勫ぇ浣滀腑鎻愬埌: 銆慭n",
862                                             p_article->username, p_article->nickname);                                             p_article->username, p_article->nickname);
863    
864                  quote_content_lines = split_data_lines(content_f, ARTICLE_QUOTE_LINE_MAX_LEN, line_offsets, ARTICLE_QUOTE_MAX_LINES + 1);                  quote_content_lines = split_data_lines(content_f, ARTICLE_QUOTE_LINE_MAX_LEN, line_offsets, ARTICLE_QUOTE_MAX_LINES + 1, 0, NULL);
865                  for (i = 0; i < quote_content_lines; i++)                  for (i = 0; i < quote_content_lines; i++)
866                  {                  {
867                          memcpy(content + len, ": ", 2); // quote line prefix                          memcpy(content + len, ": ", 2); // quote line prefix
868                          len += 2;                          len += 2;
869                          memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i]));                          memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i]));
870                          len += (line_offsets[i + 1] - line_offsets[i]);                          len += (line_offsets[i + 1] - line_offsets[i]);
871                            if (content[len - 1] != '\n') // Appennd \n if not exist
872                            {
873                                    content[len] = '\n';
874                                    len++;
875                            }
876                  }                  }
877                  if (content[len - 1] != '\n') // Appennd \n if not exist                  if (content[len - 1] != '\n') // Appennd \n if not exist
878                  {                  {
# Line 775  int article_reply(const SECTION_LIST *p_ Line 888  int article_reply(const SECTION_LIST *p_
888                  if (p_editor_data == NULL)                  if (p_editor_data == NULL)
889                  {                  {
890                          log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));                          log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
891                          ret = -3;                          ret = -1;
892                          goto cleanup;                          goto cleanup;
893                  }                  }
894    
# Line 793  int article_reply(const SECTION_LIST *p_ Line 906  int article_reply(const SECTION_LIST *p_
906          {          {
907                  clearscr();                  clearscr();
908                  moveto(21, 1);                  moveto(21, 1);
909                  prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);                  prints("鍥炲鏂囩珷浜 %s[%s] 璁ㄨ鍖猴紝鍥炲閫氱煡锛%s", p_section->stitle, p_section->sname, (reply_note ? "寮鍚" : "鍏抽棴"));
910                  moveto(22, 1);                  moveto(22, 1);
911                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));                  prints("鏍囬: %s", (p_article_new->title[0] == '\0' ? "[鏃燷" : p_article_new->title));
912                  moveto(23, 1);                  moveto(23, 1);
913                  prints("使用第 %d 个签名", sign_id);                  prints("浣跨敤绗 %d 涓鍚", sign_id);
914    
915                  if (toupper(ch) != 'T')                  if (toupper(ch) != 'T')
916                  {                  {
917                          prints("    按0~3选签名档(0表示不使用)");                          prints("    鎸0~3閫夌鍚嶆。(0琛ㄧず涓嶄娇鐢)");
918    
919                          moveto(24, 1);                          moveto(24, 1);
920                          prints("T改标题, C取消, Enter继续: ");                          prints("T鏀规爣棰, C鍙栨秷, N%s, Enter缁х画: ",
921                                       (reply_note ? "鍏抽棴鍥炲閫氱煡" : "寮鍚洖澶嶉氱煡"));
922                          iflush();                          iflush();
923                          ch = 0;                          ch = 0;
924                  }                  }
# Line 813  int article_reply(const SECTION_LIST *p_ Line 927  int article_reply(const SECTION_LIST *p_
927                  {                  {
928                          switch (toupper(ch))                          switch (toupper(ch))
929                          {                          {
930                            case KEY_NULL:
931                            case KEY_TIMEOUT:
932                                    goto cleanup;
933                          case CR:                          case CR:
                                 igetch_reset();  
934                                  break;                                  break;
935                          case 'T':                          case 'T':
936                                  moveto(24, 1);                                  len = get_data(24, 1, "鏍囬: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
                                 clrtoeol();  
                                 len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);  
937                                  for (p = title_input; *p == ' '; p++)                                  for (p = title_input; *p == ' '; p++)
938                                          ;                                          ;
939                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
# Line 836  int article_reply(const SECTION_LIST *p_ Line 950  int article_reply(const SECTION_LIST *p_
950                          case 'C':                          case 'C':
951                                  clearscr();                                  clearscr();
952                                  moveto(1, 1);                                  moveto(1, 1);
953                                  prints("取消...");                                  prints("鍙栨秷...");
954                                  press_any_key();                                  press_any_key();
955                                  goto cleanup;                                  goto cleanup;
956                            case 'N':
957                                    reply_note = (reply_note ? 0 : 1);
958                                    break;
959                          case '0':                          case '0':
960                          case '1':                          case '1':
961                          case '2':                          case '2':
# Line 863  int article_reply(const SECTION_LIST *p_ Line 980  int article_reply(const SECTION_LIST *p_
980    
981                          clearscr();                          clearscr();
982                          moveto(1, 1);                          moveto(1, 1);
983                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");                          prints("(S)鍙戦, (C)鍙栨秷, (T)鏇存敼鏍囬 or (E)鍐嶇紪杈? [S]: ");
984                          iflush();                          iflush();
985    
986                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
987                          {                          {
988                                  switch (toupper(ch))                                  switch (toupper(ch))
989                                  {                                  {
990                                    case KEY_NULL:
991                                    case KEY_TIMEOUT:
992                                            goto cleanup;
993                                  case CR:                                  case CR:
                                         igetch_reset();  
994                                  case 'S':                                  case 'S':
995                                          break;                                          break;
996                                  case 'C':                                  case 'C':
997                                          clearscr();                                          clearscr();
998                                          moveto(1, 1);                                          moveto(1, 1);
999                                          prints("取消...");                                          prints("鍙栨秷...");
1000                                          press_any_key();                                          press_any_key();
1001                                          goto cleanup;                                          goto cleanup;
1002                                  case 'T':                                  case 'T':
# Line 898  int article_reply(const SECTION_LIST *p_ Line 1017  int article_reply(const SECTION_LIST *p_
1017                  }                  }
1018          }          }
1019    
1020            if (SYS_server_exit) // Do not save data on shutdown
1021            {
1022                    goto cleanup;
1023            }
1024    
1025          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
1026          if (content == NULL)          if (content == NULL)
1027          {          {
# Line 910  int article_reply(const SECTION_LIST *p_ Line 1034  int article_reply(const SECTION_LIST *p_
1034          if (len_content < 0)          if (len_content < 0)
1035          {          {
1036                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
1037                  ret = -2;                  ret = -1;
1038                  goto cleanup;                  goto cleanup;
1039          }          }
1040    
# Line 951  int article_reply(const SECTION_LIST *p_ Line 1075  int article_reply(const SECTION_LIST *p_
1075                  rs = NULL;                  rs = NULL;
1076          }          }
1077    
1078          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
1079          if (content_f == NULL)          content_display_length = str_length(content, 1);
         {  
                 log_error("malloc(content_f) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
   
         sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);  
         if (sql_content == NULL)  
         {  
                 log_error("malloc(sql_content) error: OOM\n");  
                 ret = -1;  
                 goto cleanup;  
         }  
1080    
1081          // Begin transaction          // Begin transaction
1082          if (mysql_query(db, "SET autocommit=0") != 0)          if (mysql_query(db, "SET autocommit=0") != 0)
# Line 983  int article_reply(const SECTION_LIST *p_ Line 1094  int article_reply(const SECTION_LIST *p_
1094          }          }
1095    
1096          // Secure SQL parameters          // Secure SQL parameters
1097            content_f = malloc((size_t)len_content * 2 + 1);
1098            if (content_f == NULL)
1099            {
1100                    log_error("malloc(content_f) error: OOM\n");
1101                    ret = -1;
1102                    goto cleanup;
1103            }
1104    
1105          mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));          mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
1106          mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));          mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
1107          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
1108    
1109            free(content);
1110            content = NULL;
1111    
1112          // Add content          // Add content
1113          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
1114            if (sql_content == NULL)
1115            {
1116                    log_error("malloc(sql_content) error: OOM\n");
1117                    ret = -1;
1118                    goto cleanup;
1119            }
1120    
1121            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
1122                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1123                           content_f);                           content_f);
1124    
1125          if (mysql_query(db, sql) != 0)          free(content_f);
1126            content_f = NULL;
1127    
1128            if (mysql_query(db, sql_content) != 0)
1129          {          {
1130                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
1131                  ret = -1;                  ret = -1;
# Line 1001  int article_reply(const SECTION_LIST *p_ Line 1134  int article_reply(const SECTION_LIST *p_
1134    
1135          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
1136    
1137            free(sql_content);
1138            sql_content = NULL;
1139    
1140          // Add article          // Add article
1141          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
1142                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
1143                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
1144                           "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %ld)",                           "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', %d, %d, NOW(), 1, %d)",
1145                           p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),                           p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1146                           BBS_priv.uid, BBS_username, nickname_f, title_f,                           BBS_priv.uid, BBS_username, nickname_f, title_f,
1147                           p_article_new->cid, hostaddr_client, BBS_user_exp, len_content);                           p_article_new->cid, hostaddr_client,
1148                             reply_note, BBS_user_exp, content_display_length);
1149    
1150          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
1151          {          {
# Line 1046  int article_reply(const SECTION_LIST *p_ Line 1183  int article_reply(const SECTION_LIST *p_
1183                  goto cleanup;                  goto cleanup;
1184          }          }
1185    
1186            // Notify the authors of the topic / article which is replyed.
1187            snprintf(sql, sizeof(sql),
1188                             "SELECT DISTINCT UID FROM bbs WHERE (AID = %d OR AID = %d) "
1189                             "AND visible AND reply_note AND UID <> %d",
1190                             p_article->tid, p_article->aid, BBS_priv.uid);
1191    
1192            if (mysql_query(db, sql) != 0)
1193            {
1194                    log_error("Read reply info error: %s\n", mysql_error(db));
1195                    ret = -1;
1196                    goto cleanup;
1197            }
1198            if ((rs = mysql_store_result(db)) == NULL)
1199            {
1200                    log_error("Get reply info failed\n");
1201                    ret = -1;
1202                    goto cleanup;
1203            }
1204    
1205            while ((row = mysql_fetch_row(rs)))
1206            {
1207                    // Send notification message
1208                    len_msg = snprintf(msg, BBS_msg_max_len,
1209                                                       "鏈変汉鍥炲浜嗘偍鎵鍙戣〃/鍥炲鐨勬枃绔狅紝蹇潵"
1210                                                       "[article %d]鐪嬬湅[/article]銆%s銆嬪惂锛乗n",
1211                                                       p_article_new->aid, title_f);
1212    
1213                    mysql_real_escape_string(db, msg_f, msg, (unsigned long)len_msg);
1214    
1215                    snprintf(sql, sizeof(sql),
1216                                     "INSERT INTO bbs_msg(fromUID, toUID, content, send_dt, send_ip) "
1217                                     "VALUES(%d, %d, '%s', NOW(), '%s')",
1218                                     BBS_sys_id, atoi(row[0]), msg_f, hostaddr_client);
1219    
1220                    if (mysql_query(db, sql) != 0)
1221                    {
1222                            log_error("Insert msg error: %s\n", mysql_error(db));
1223                            ret = -1;
1224                            goto cleanup;
1225                    }
1226            }
1227            mysql_free_result(rs);
1228            rs = NULL;
1229    
1230          // Add exp          // Add exp
1231          if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section          if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
1232          {          {
# Line 1082  int article_reply(const SECTION_LIST *p_ Line 1263  int article_reply(const SECTION_LIST *p_
1263                  goto cleanup;                  goto cleanup;
1264          }          }
1265    
1266            mysql_close(db);
1267            db = NULL;
1268    
1269          clearscr();          clearscr();
1270          moveto(1, 1);          moveto(1, 1);
1271          prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);          prints("鍙戦佸畬鎴愶紝鏂版枃绔犻氬父浼氬湪%d绉掑悗鍙", BBS_section_list_load_interval);
1272          press_any_key();          press_any_key();
1273          ret = 1; // Success          ret = 1; // Success
1274    


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

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