/[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.9 by sysadm, Sat Jun 14 11:15:46 2025 UTC Revision 1.28 by sysadm, Fri Oct 10 01:55:06 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          long len;          long len;
# Line 63  int article_post(const SECTION_LIST *p_s Line 62  int article_post(const SECTION_LIST *p_s
62                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
63          }          }
64    
65            if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
66            {
67                    clearscr();
68                    moveto(1, 1);
69                    prints("您没有权限在本版块发表文章\n");
70                    press_any_key();
71    
72                    return 0;
73            }
74    
75          p_article_new->title[0] = '\0';          p_article_new->title[0] = '\0';
76          title_input[0] = '\0';          title_input[0] = '\0';
77          p_article_new->transship = 0;          p_article_new->transship = 0;
# Line 71  int article_post(const SECTION_LIST *p_s Line 80  int article_post(const SECTION_LIST *p_s
80          if (p_editor_data == NULL)          if (p_editor_data == NULL)
81          {          {
82                  log_error("editor_data_load() error\n");                  log_error("editor_data_load() error\n");
83                  return -2;                  ret = -1;
84                    goto cleanup;
85          }          }
86    
87          // Set title and sign          // Set title and sign
# Line 79  int article_post(const SECTION_LIST *p_s Line 89  int article_post(const SECTION_LIST *p_s
89          {          {
90                  clearscr();                  clearscr();
91                  moveto(21, 1);                  moveto(21, 1);
92                  prints(" %s[%s] : %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "ת" : "ԭ"));                  prints("发表文章于 %s[%s] 讨论区,类型: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"));
93                  moveto(22, 1);                  moveto(22, 1);
94                  prints(": %s", (p_article_new->title[0] == '\0' ? "[]" : p_article_new->title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
95                  moveto(23, 1);                  moveto(23, 1);
96                  prints("ʹõ %d ǩ", sign_id);                  prints("使用第 %d 个签名", sign_id);
97    
98                  if (toupper(ch) != 'T')                  if (toupper(ch) != 'T')
99                  {                  {
100                          prints("    0~3ѡǩ(0ʾʹ)");                          prints("    按0~3选签名档(0表示不使用)");
101    
102                          moveto(24, 1);                          moveto(24, 1);
103                          prints("Tı, Cȡ, ZΪת, YΪԭ, Enter: ");                          prints("T改标题, C取消, Z设为转载, Y设为原创, Enter继续: ");
104                          iflush();                          iflush();
105                          ch = 0;                          ch = 0;
106                  }                  }
# Line 99  int article_post(const SECTION_LIST *p_s Line 109  int article_post(const SECTION_LIST *p_s
109                  {                  {
110                          switch (toupper(ch))                          switch (toupper(ch))
111                          {                          {
112                            case KEY_NULL:
113                            case KEY_TIMEOUT:
114                                    goto cleanup;
115                          case CR:                          case CR:
116                                  igetch_reset();                                  igetch_reset();
117                                  break;                                  break;
118                          case 'T':                          case 'T':
119                                  moveto(24, 1);                                  moveto(24, 1);
120                                  clrtoeol();                                  clrtoeol();
121                                  len = get_data(24, 1, ": ", title_input, TITLE_INPUT_MAX_LEN, 1);                                  len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
122                                  for (p = title_input; *p == ' '; p++)                                  for (p = title_input; *p == ' '; p++)
123                                          ;                                          ;
124                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
# Line 122  int article_post(const SECTION_LIST *p_s Line 135  int article_post(const SECTION_LIST *p_s
135                          case 'C':                          case 'C':
136                                  clearscr();                                  clearscr();
137                                  moveto(1, 1);                                  moveto(1, 1);
138                                  prints("ȡ...");                                  prints("取消...");
139                                  press_any_key();                                  press_any_key();
140                                  goto cleanup;                                  goto cleanup;
141                          case 'Y':                          case 'Y':
# Line 155  int article_post(const SECTION_LIST *p_s Line 168  int article_post(const SECTION_LIST *p_s
168    
169                          clearscr();                          clearscr();
170                          moveto(1, 1);                          moveto(1, 1);
171                          prints("(S), (C)ȡ, (T)ı or (E)ٱ༭? [S]: ");                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
172                          iflush();                          iflush();
173    
174                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
175                          {                          {
176                                  switch (toupper(ch))                                  switch (toupper(ch))
177                                  {                                  {
178                                    case KEY_NULL:
179                                    case KEY_TIMEOUT:
180                                            goto cleanup;
181                                  case CR:                                  case CR:
182                                          igetch_reset();                                          igetch_reset();
183                                  case 'S':                                  case 'S':
# Line 169  int article_post(const SECTION_LIST *p_s Line 185  int article_post(const SECTION_LIST *p_s
185                                  case 'C':                                  case 'C':
186                                          clearscr();                                          clearscr();
187                                          moveto(1, 1);                                          moveto(1, 1);
188                                          prints("ȡ...");                                          prints("取消...");
189                                          press_any_key();                                          press_any_key();
190                                          goto cleanup;                                          goto cleanup;
191                                  case 'T':                                  case 'T':
# Line 190  int article_post(const SECTION_LIST *p_s Line 206  int article_post(const SECTION_LIST *p_s
206                  }                  }
207          }          }
208    
209            if (SYS_server_exit) // Do not save data on shutdown
210            {
211                    goto cleanup;
212            }
213    
214          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
215          if (content == NULL)          if (content == NULL)
216          {          {
# Line 202  int article_post(const SECTION_LIST *p_s Line 223  int article_post(const SECTION_LIST *p_s
223          if (len_content < 0)          if (len_content < 0)
224          {          {
225                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
226                  ret = -2;                  ret = -1;
227                  goto cleanup;                  goto cleanup;
228          }          }
229    
# Line 243  int article_post(const SECTION_LIST *p_s Line 264  int article_post(const SECTION_LIST *p_s
264                  rs = NULL;                  rs = NULL;
265          }          }
266    
267          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
268          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;  
         }  
269    
270          // Begin transaction          // Begin transaction
271          if (mysql_query(db, "SET autocommit=0") != 0)          if (mysql_query(db, "SET autocommit=0") != 0)
# Line 275  int article_post(const SECTION_LIST *p_s Line 283  int article_post(const SECTION_LIST *p_s
283          }          }
284    
285          // Secure SQL parameters          // Secure SQL parameters
286            content_f = malloc((size_t)len_content * 2 + 1);
287            if (content_f == NULL)
288            {
289                    log_error("malloc(content_f) error: OOM\n");
290                    ret = -1;
291                    goto cleanup;
292            }
293    
294          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)));
295          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)));
296          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
297    
298            free(content);
299            content = NULL;
300    
301          // Add content          // Add content
302          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
303            if (sql_content == NULL)
304            {
305                    log_error("malloc(sql_content) error: OOM\n");
306                    ret = -1;
307                    goto cleanup;
308            }
309    
310            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
311                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",
312                           content_f);                           content_f);
313    
314          if (mysql_query(db, sql) != 0)          free(content_f);
315            content_f = NULL;
316    
317            if (mysql_query(db, sql_content) != 0)
318          {          {
319                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
320                  ret = -1;                  ret = -1;
# Line 293  int article_post(const SECTION_LIST *p_s Line 323  int article_post(const SECTION_LIST *p_s
323    
324          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
325    
326            free(sql_content);
327            sql_content = NULL;
328    
329          // Add article          // Add article
330          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
331                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
332                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
333                           "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', 1, %d, NOW(), 1, %d)",
334                           p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,                           p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,
335                           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, BBS_user_exp, content_display_length);
336    
337          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
338          {          {
# Line 358  int article_post(const SECTION_LIST *p_s Line 391  int article_post(const SECTION_LIST *p_s
391                  goto cleanup;                  goto cleanup;
392          }          }
393    
394            mysql_close(db);
395            db = NULL;
396    
397          clearscr();          clearscr();
398          moveto(1, 1);          moveto(1, 1);
399          prints("ɣͨ%dɼ", BBS_section_list_load_interval);          prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
400          press_any_key();          press_any_key();
401          ret = 1; // Success          ret = 1; // Success
402    
# Line 387  int article_modify(const SECTION_LIST *p Line 423  int article_modify(const SECTION_LIST *p
423          char *content = NULL;          char *content = NULL;
424          char *content_f = NULL;          char *content_f = NULL;
425          long len_content;          long len_content;
426            int content_display_length;
427          int ch;          int ch;
428          long ret = 0;          long ret = 0;
429          time_t now;          time_t now;
# Line 404  int article_modify(const SECTION_LIST *p Line 441  int article_modify(const SECTION_LIST *p
441          {          {
442                  clearscr();                  clearscr();
443                  moveto(1, 1);                  moveto(1, 1);
444                  prints("޷༭ϵ");                  prints("该文章无法被编辑,请联系版主。");
445                  press_any_key();                  press_any_key();
446    
447                  return 0;                  return 0;
# Line 414  int article_modify(const SECTION_LIST *p Line 451  int article_modify(const SECTION_LIST *p
451          if (db == NULL)          if (db == NULL)
452          {          {
453                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
454                  return -1;                  ret = -1;
455                    goto cleanup;
456          }          }
457    
458          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 426  int article_modify(const SECTION_LIST *p Line 464  int article_modify(const SECTION_LIST *p
464          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
465          {          {
466                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
467                  ret = -2;                  ret = -1;
468                  goto cleanup;                  goto cleanup;
469          }          }
470          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
471          {          {
472                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
473                  ret = -2;                  ret = -1;
474                  goto cleanup;                  goto cleanup;
475          }          }
476    
477          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
478          {          {
479                  p_editor_data = editor_data_load(row[1]);                  content = malloc(ARTICLE_CONTENT_MAX_LEN);
480                    if (content == NULL)
481                    {
482                            log_error("malloc(content) error: OOM\n");
483                            ret = -1;
484                            goto cleanup;
485                    }
486    
487                    strncpy(content, row[1], ARTICLE_CONTENT_MAX_LEN - 1);
488                    content[ARTICLE_CONTENT_MAX_LEN - 1] = '\0';
489    
490                    // Remove control sequence
491                    len_content = str_filter(content, 0);
492    
493                    p_editor_data = editor_data_load(content);
494                  if (p_editor_data == NULL)                  if (p_editor_data == NULL)
495                  {                  {
496                          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]));
497                          ret = -3;                          ret = -1;
498                          goto cleanup;                          goto cleanup;
499                  }                  }
500    
501                    free(content);
502                    content = NULL;
503          }          }
504          mysql_free_result(rs);          mysql_free_result(rs);
505          rs = NULL;          rs = NULL;
# Line 458  int article_modify(const SECTION_LIST *p Line 513  int article_modify(const SECTION_LIST *p
513    
514                  clearscr();                  clearscr();
515                  moveto(1, 1);                  moveto(1, 1);
516                  prints("(S), (C)ȡ or (E)ٱ༭? [S]: ");                  prints("(S)保存, (C)取消 or (E)再编辑? [S]: ");
517                  iflush();                  iflush();
518    
519                  for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                  for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
520                  {                  {
521                          switch (toupper(ch))                          switch (toupper(ch))
522                          {                          {
523                            case KEY_NULL:
524                            case KEY_TIMEOUT:
525                                    goto cleanup;
526                          case CR:                          case CR:
527                                  igetch_reset();                                  igetch_reset();
528                          case 'S':                          case 'S':
# Line 472  int article_modify(const SECTION_LIST *p Line 530  int article_modify(const SECTION_LIST *p
530                          case 'C':                          case 'C':
531                                  clearscr();                                  clearscr();
532                                  moveto(1, 1);                                  moveto(1, 1);
533                                  prints("ȡ...");                                  prints("取消...");
534                                  press_any_key();                                  press_any_key();
535                                  goto cleanup;                                  goto cleanup;
536                          case 'E':                          case 'E':
# Line 485  int article_modify(const SECTION_LIST *p Line 543  int article_modify(const SECTION_LIST *p
543                  }                  }
544          }          }
545    
546            if (SYS_server_exit) // Do not save data on shutdown
547            {
548                    goto cleanup;
549            }
550    
551          // Allocate buffers in big size          // Allocate buffers in big size
552          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
553          if (content == NULL)          if (content == NULL)
# Line 498  int article_modify(const SECTION_LIST *p Line 561  int article_modify(const SECTION_LIST *p
561          if (len_content < 0)          if (len_content < 0)
562          {          {
563                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
564                  ret = -2;                  ret = -1;
565                  goto cleanup;                  goto cleanup;
566          }          }
567    
# Line 507  int article_modify(const SECTION_LIST *p Line 570  int article_modify(const SECTION_LIST *p
570          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);
571    
572          len_content += snprintf(content + len_content, LINE_BUFFER_LEN,          len_content += snprintf(content + len_content, LINE_BUFFER_LEN,
573                                                          "\n--\n %s ޸ıġ\n",                                                          "\n--\n※ 作者已于 %s 修改本文※\n",
574                                                          str_modify_dt);                                                          str_modify_dt);
575    
576          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
577          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;  
         }  
578    
579          db = db_open();          db = db_open();
580          if (db == NULL)          if (db == NULL)
# Line 550  int article_modify(const SECTION_LIST *p Line 600  int article_modify(const SECTION_LIST *p
600          }          }
601    
602          // Secure SQL parameters          // Secure SQL parameters
603            content_f = malloc((size_t)len_content * 2 + 1);
604            if (content_f == NULL)
605            {
606                    log_error("malloc(content_f) error: OOM\n");
607                    ret = -1;
608                    goto cleanup;
609            }
610    
611          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
612    
613            free(content);
614            content = NULL;
615    
616          // Add content          // Add content
617          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
618            if (sql_content == NULL)
619            {
620                    log_error("malloc(sql_content) error: OOM\n");
621                    ret = -1;
622                    goto cleanup;
623            }
624    
625            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
626                           "INSERT INTO bbs_content(AID, content) values(%d, '%s')",                           "INSERT INTO bbs_content(AID, content) values(%d, '%s')",
627                           p_article->aid, content_f);                           p_article->aid, content_f);
628    
629          if (mysql_query(db, sql) != 0)          free(content_f);
630            content_f = NULL;
631    
632            if (mysql_query(db, sql_content) != 0)
633          {          {
634                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
635                  ret = -1;                  ret = -1;
# Line 566  int article_modify(const SECTION_LIST *p Line 638  int article_modify(const SECTION_LIST *p
638    
639          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
640    
641            free(sql_content);
642            sql_content = NULL;
643    
644          // Update article          // Update article
645          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
646                           "UPDATE bbs SET CID = %d, length = %ld WHERE AID = %d",                           "UPDATE bbs SET CID = %d, length = %d, excerption = 0 WHERE AID = %d", // Set excerption = 0 explictly in case of rare condition
647                           p_article_new->cid, len_content, p_article->aid);                           p_article_new->cid, content_display_length, p_article->aid);
648    
649          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
650          {          {
# Line 606  int article_modify(const SECTION_LIST *p Line 681  int article_modify(const SECTION_LIST *p
681                  goto cleanup;                  goto cleanup;
682          }          }
683    
684            mysql_close(db);
685            db = NULL;
686    
687          clearscr();          clearscr();
688          moveto(1, 1);          moveto(1, 1);
689          prints("޸ɣͨ%dɼ", BBS_section_list_load_interval);          prints("修改完成,新内容通常会在%d秒后可见", BBS_section_list_load_interval);
690          press_any_key();          press_any_key();
691          ret = 1; // Success          ret = 1; // Success
692    
693  cleanup:  cleanup:
694            mysql_free_result(rs);
695          mysql_close(db);          mysql_close(db);
696    
697          // Cleanup buffers          // Cleanup buffers
# Line 639  int article_reply(const SECTION_LIST *p_ Line 718  int article_reply(const SECTION_LIST *p_
718          char *content = NULL;          char *content = NULL;
719          char *content_f = NULL;          char *content_f = NULL;
720          long len_content;          long len_content;
721            int content_display_length;
722          char nickname_f[BBS_nickname_max_len * 2 + 1];          char nickname_f[BBS_nickname_max_len * 2 + 1];
723          int sign_id = 0;          int sign_id = 0;
724          long len;          long len;
# Line 649  int article_reply(const SECTION_LIST *p_ Line 729  int article_reply(const SECTION_LIST *p_
729          long quote_content_lines;          long quote_content_lines;
730          long i;          long i;
731          long ret = 0;          long ret = 0;
732            int topic_locked = 0;
733            char msg[BBS_msg_max_len];
734            char msg_f[BBS_msg_max_len * 2 + 1];
735            int len_msg;
736    
737          if (p_section == NULL || p_article == NULL)          if (p_section == NULL || p_article == NULL)
738          {          {
739                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
740          }          }
741    
742          if (p_article->lock) // Reply is not allowed          if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
743          {          {
744                  clearscr();                  clearscr();
745                  moveto(1, 1);                  moveto(1, 1);
746                  prints("лظ");                  prints("您没有权限在本版块发表文章\n");
747                  press_any_key();                  press_any_key();
748    
749                  return 0;                  return 0;
# Line 667  int article_reply(const SECTION_LIST *p_ Line 751  int article_reply(const SECTION_LIST *p_
751    
752          p_article_new->title[0] = '\0';          p_article_new->title[0] = '\0';
753          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
754          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);
755          title_input[len] = '\0';          title_input[len] = '\0';
756    
757          db = db_open();          db = db_open();
758          if (db == NULL)          if (db == NULL)
759          {          {
760                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
761                  return -1;                  ret = -1;
762                    goto cleanup;
763            }
764    
765            snprintf(sql, sizeof(sql),
766                             "SELECT `lock` FROM bbs WHERE AID = %d",
767                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
768    
769            if (mysql_query(db, sql) != 0)
770            {
771                    log_error("Query article status error: %s\n", mysql_error(db));
772                    ret = -1;
773                    goto cleanup;
774            }
775            if ((rs = mysql_store_result(db)) == NULL)
776            {
777                    log_error("Get article status data failed\n");
778                    ret = -1;
779                    goto cleanup;
780            }
781    
782            if ((row = mysql_fetch_row(rs)))
783            {
784                    if (atoi(row[0]) != 0)
785                    {
786                            topic_locked = 1;
787                    }
788            }
789            mysql_free_result(rs);
790            rs = NULL;
791    
792            if (topic_locked) // Reply is not allowed
793            {
794                    mysql_close(db);
795                    db = NULL;
796    
797                    clearscr();
798                    moveto(1, 1);
799                    prints("该主题谢绝回复");
800                    press_any_key();
801    
802                    goto cleanup;
803          }          }
804    
805          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 686  int article_reply(const SECTION_LIST *p_ Line 811  int article_reply(const SECTION_LIST *p_
811          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
812          {          {
813                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
814                  return -2;                  ret = -1;
815                    goto cleanup;
816          }          }
817          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
818          {          {
819                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
820                  return -2;                  ret = -1;
821                    goto cleanup;
822          }          }
823    
824          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
# Line 713  int article_reply(const SECTION_LIST *p_ Line 840  int article_reply(const SECTION_LIST *p_
840                  }                  }
841    
842                  // Apply LML render to content body                  // Apply LML render to content body
843                  len = lml_plain(row[1], content_f, ARTICLE_CONTENT_MAX_LEN);                  len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 0);
844                  content_f[len] = '\0';                  content_f[len] = '\0';
845    
846                  // Remove control sequence                  // Remove control sequence
847                  len = ctrl_seq_filter(content_f);                  len = str_filter(content_f, 0);
848    
849                  len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,                  len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
850                                             "\n\n %s (%s) Ĵᵽ: \n",                                             "\n\n【 在 %s (%s) 的大作中提到: 】\n",
851                                             p_article->username, p_article->nickname);                                             p_article->username, p_article->nickname);
852    
853                  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);
854                  for (i = 0; i < quote_content_lines; i++)                  for (i = 0; i < quote_content_lines; i++)
855                  {                  {
856                          memcpy(content + len, ": ", 2); // quote line prefix                          memcpy(content + len, ": ", 2); // quote line prefix
857                          len += 2;                          len += 2;
858                          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]));
859                          len += (line_offsets[i + 1] - line_offsets[i]);                          len += (line_offsets[i + 1] - line_offsets[i]);
860                            if (content[len - 1] != '\n') // Appennd \n if not exist
861                            {
862                                    content[len] = '\n';
863                                    len++;
864                            }
865                  }                  }
866                  if (content[len - 1] != '\n') // Appennd \n if not exist                  if (content[len - 1] != '\n') // Appennd \n if not exist
867                  {                  {
# Line 745  int article_reply(const SECTION_LIST *p_ Line 877  int article_reply(const SECTION_LIST *p_
877                  if (p_editor_data == NULL)                  if (p_editor_data == NULL)
878                  {                  {
879                          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]));
880                          ret = -3;                          ret = -1;
881                          goto cleanup;                          goto cleanup;
882                  }                  }
883    
# Line 763  int article_reply(const SECTION_LIST *p_ Line 895  int article_reply(const SECTION_LIST *p_
895          {          {
896                  clearscr();                  clearscr();
897                  moveto(21, 1);                  moveto(21, 1);
898                  prints("ظ %s[%s] ", p_section->stitle, p_section->sname);                  prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);
899                  moveto(22, 1);                  moveto(22, 1);
900                  prints(": %s", (p_article_new->title[0] == '\0' ? "[]" : p_article_new->title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
901                  moveto(23, 1);                  moveto(23, 1);
902                  prints("ʹõ %d ǩ", sign_id);                  prints("使用第 %d 个签名", sign_id);
903    
904                  if (toupper(ch) != 'T')                  if (toupper(ch) != 'T')
905                  {                  {
906                          prints("    0~3ѡǩ(0ʾʹ)");                          prints("    按0~3选签名档(0表示不使用)");
907    
908                          moveto(24, 1);                          moveto(24, 1);
909                          prints("Tı, Cȡ, Enter: ");                          prints("T改标题, C取消, Enter继续: ");
910                          iflush();                          iflush();
911                          ch = 0;                          ch = 0;
912                  }                  }
# Line 783  int article_reply(const SECTION_LIST *p_ Line 915  int article_reply(const SECTION_LIST *p_
915                  {                  {
916                          switch (toupper(ch))                          switch (toupper(ch))
917                          {                          {
918                            case KEY_NULL:
919                            case KEY_TIMEOUT:
920                                    goto cleanup;
921                          case CR:                          case CR:
922                                  igetch_reset();                                  igetch_reset();
923                                  break;                                  break;
924                          case 'T':                          case 'T':
925                                  moveto(24, 1);                                  moveto(24, 1);
926                                  clrtoeol();                                  clrtoeol();
927                                  len = get_data(24, 1, ": ", title_input, TITLE_INPUT_MAX_LEN, 1);                                  len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
928                                  for (p = title_input; *p == ' '; p++)                                  for (p = title_input; *p == ' '; p++)
929                                          ;                                          ;
930                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)                                  for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
# Line 806  int article_reply(const SECTION_LIST *p_ Line 941  int article_reply(const SECTION_LIST *p_
941                          case 'C':                          case 'C':
942                                  clearscr();                                  clearscr();
943                                  moveto(1, 1);                                  moveto(1, 1);
944                                  prints("ȡ...");                                  prints("取消...");
945                                  press_any_key();                                  press_any_key();
946                                  goto cleanup;                                  goto cleanup;
947                          case '0':                          case '0':
# Line 833  int article_reply(const SECTION_LIST *p_ Line 968  int article_reply(const SECTION_LIST *p_
968    
969                          clearscr();                          clearscr();
970                          moveto(1, 1);                          moveto(1, 1);
971                          prints("(S), (C)ȡ, (T)ı or (E)ٱ༭? [S]: ");                          prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
972                          iflush();                          iflush();
973    
974                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))                          for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
975                          {                          {
976                                  switch (toupper(ch))                                  switch (toupper(ch))
977                                  {                                  {
978                                    case KEY_NULL:
979                                    case KEY_TIMEOUT:
980                                            goto cleanup;
981                                  case CR:                                  case CR:
982                                          igetch_reset();                                          igetch_reset();
983                                  case 'S':                                  case 'S':
# Line 847  int article_reply(const SECTION_LIST *p_ Line 985  int article_reply(const SECTION_LIST *p_
985                                  case 'C':                                  case 'C':
986                                          clearscr();                                          clearscr();
987                                          moveto(1, 1);                                          moveto(1, 1);
988                                          prints("ȡ...");                                          prints("取消...");
989                                          press_any_key();                                          press_any_key();
990                                          goto cleanup;                                          goto cleanup;
991                                  case 'T':                                  case 'T':
# Line 868  int article_reply(const SECTION_LIST *p_ Line 1006  int article_reply(const SECTION_LIST *p_
1006                  }                  }
1007          }          }
1008    
1009            if (SYS_server_exit) // Do not save data on shutdown
1010            {
1011                    goto cleanup;
1012            }
1013    
1014          content = malloc(ARTICLE_CONTENT_MAX_LEN);          content = malloc(ARTICLE_CONTENT_MAX_LEN);
1015          if (content == NULL)          if (content == NULL)
1016          {          {
# Line 880  int article_reply(const SECTION_LIST *p_ Line 1023  int article_reply(const SECTION_LIST *p_
1023          if (len_content < 0)          if (len_content < 0)
1024          {          {
1025                  log_error("editor_data_save() error\n");                  log_error("editor_data_save() error\n");
1026                  ret = -2;                  ret = -1;
1027                  goto cleanup;                  goto cleanup;
1028          }          }
1029    
# Line 921  int article_reply(const SECTION_LIST *p_ Line 1064  int article_reply(const SECTION_LIST *p_
1064                  rs = NULL;                  rs = NULL;
1065          }          }
1066    
1067          content_f = malloc((size_t)len_content * 2 + 1);          // Calculate display length of content
1068          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;  
         }  
1069    
1070          // Begin transaction          // Begin transaction
1071          if (mysql_query(db, "SET autocommit=0") != 0)          if (mysql_query(db, "SET autocommit=0") != 0)
# Line 953  int article_reply(const SECTION_LIST *p_ Line 1083  int article_reply(const SECTION_LIST *p_
1083          }          }
1084    
1085          // Secure SQL parameters          // Secure SQL parameters
1086            content_f = malloc((size_t)len_content * 2 + 1);
1087            if (content_f == NULL)
1088            {
1089                    log_error("malloc(content_f) error: OOM\n");
1090                    ret = -1;
1091                    goto cleanup;
1092            }
1093    
1094          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)));
1095          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)));
1096          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);          mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
1097    
1098            free(content);
1099            content = NULL;
1100    
1101          // Add content          // Add content
1102          snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,          sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
1103            if (sql_content == NULL)
1104            {
1105                    log_error("malloc(sql_content) error: OOM\n");
1106                    ret = -1;
1107                    goto cleanup;
1108            }
1109    
1110            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
1111                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",                           "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1112                           content_f);                           content_f);
1113    
1114          if (mysql_query(db, sql) != 0)          free(content_f);
1115            content_f = NULL;
1116    
1117            if (mysql_query(db, sql_content) != 0)
1118          {          {
1119                  log_error("Add article content error: %s\n", mysql_error(db));                  log_error("Add article content error: %s\n", mysql_error(db));
1120                  ret = -1;                  ret = -1;
# Line 971  int article_reply(const SECTION_LIST *p_ Line 1123  int article_reply(const SECTION_LIST *p_
1123    
1124          p_article_new->cid = (int32_t)mysql_insert_id(db);          p_article_new->cid = (int32_t)mysql_insert_id(db);
1125    
1126            free(sql_content);
1127            sql_content = NULL;
1128    
1129          // Add article          // Add article
1130          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
1131                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "                           "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
1132                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "                           "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
1133                           "VALUES(%d, 0, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %ld)",                           "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %d)",
1134                           p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,                           p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1135                           p_article_new->cid, hostaddr_client, BBS_user_exp, len_content);                           BBS_priv.uid, BBS_username, nickname_f, title_f,
1136                             p_article_new->cid, hostaddr_client, BBS_user_exp, content_display_length);
1137    
1138          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
1139          {          {
# Line 992  int article_reply(const SECTION_LIST *p_ Line 1148  int article_reply(const SECTION_LIST *p_
1148          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
1149                           "UPDATE bbs SET reply_count = reply_count + 1, "                           "UPDATE bbs SET reply_count = reply_count + 1, "
1150                           "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "                           "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "
1151                           "last_reply_nickname = '%s' WHERE Aid = %d",                           "last_reply_nickname = '%s' WHERE AID = %d",
1152                           BBS_priv.uid, BBS_username, nickname_f, p_article->aid);                           BBS_priv.uid, BBS_username, nickname_f,
1153                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
1154    
1155            if (mysql_query(db, sql) != 0)
1156            {
1157                    log_error("Update topic article error: %s\n", mysql_error(db));
1158                    ret = -1;
1159                    goto cleanup;
1160            }
1161    
1162          // Link content to article          // Link content to article
1163          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 1007  int article_reply(const SECTION_LIST *p_ Line 1171  int article_reply(const SECTION_LIST *p_
1171                  goto cleanup;                  goto cleanup;
1172          }          }
1173    
1174            // Notify the authors of the topic / article which is replyed.
1175            snprintf(sql, sizeof(sql),
1176                             "SELECT DISTINCT UID FROM bbs WHERE (AID = %d OR AID = %d) "
1177                             "AND visible AND reply_note AND UID <> %d",
1178                             p_article->tid, p_article->aid, BBS_priv.uid);
1179    
1180            if (mysql_query(db, sql) != 0)
1181            {
1182                    log_error("Read reply info error: %s\n", mysql_error(db));
1183                    ret = -1;
1184                    goto cleanup;
1185            }
1186            if ((rs = mysql_store_result(db)) == NULL)
1187            {
1188                    log_error("Get reply info failed\n");
1189                    ret = -1;
1190                    goto cleanup;
1191            }
1192    
1193            while ((row = mysql_fetch_row(rs)))
1194            {
1195                    // Send notification message
1196                    len_msg = snprintf(msg, BBS_msg_max_len,
1197                                                       "[hide]SYS_Reply_Article[/hide]有人回复了您所发表/回复的文章,快来"
1198                                                       "[article %d]看看[/article]《%s》吧!\n",
1199                                                       p_article_new->aid, title_f);
1200    
1201                    mysql_real_escape_string(db, msg_f, msg, (unsigned long)len_msg);
1202    
1203                    snprintf(sql, sizeof(sql),
1204                                     "INSERT INTO bbs_msg(fromUID, toUID, content, send_dt, send_ip) "
1205                                     "VALUES(%d, %d, '%s', NOW(), '%s')",
1206                                     BBS_sys_id, atoi(row[0]), msg_f, hostaddr_client);
1207    
1208                    if (mysql_query(db, sql) != 0)
1209                    {
1210                            log_error("Insert msg error: %s\n", mysql_error(db));
1211                            ret = -1;
1212                            goto cleanup;
1213                    }
1214            }
1215            mysql_free_result(rs);
1216            rs = NULL;
1217    
1218          // Add exp          // Add exp
1219          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
1220          {          {
# Line 1043  int article_reply(const SECTION_LIST *p_ Line 1251  int article_reply(const SECTION_LIST *p_
1251                  goto cleanup;                  goto cleanup;
1252          }          }
1253    
1254            mysql_close(db);
1255            db = NULL;
1256    
1257          clearscr();          clearscr();
1258          moveto(1, 1);          moveto(1, 1);
1259          prints("ɣͨ%dɼ", BBS_section_list_load_interval);          prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1260          press_any_key();          press_any_key();
1261          ret = 1; // Success          ret = 1; // Success
1262    


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

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