/[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.7 by sysadm, Sat Jun 14 02:58:11 2025 UTC Revision 1.21 by sysadm, Tue Jun 17 13:17:04 2025 UTC
# Line 18  Line 18 
18  #include "article_cache.h"  #include "article_cache.h"
19  #include "editor.h"  #include "editor.h"
20  #include "screen.h"  #include "screen.h"
21    #include "bbs.h"
22  #include "log.h"  #include "log.h"
23  #include "io.h"  #include "io.h"
24  #include "lml.h"  #include "lml.h"
25  #include "database.h"  #include "database.h"
26    #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>
31    
32  #define TITLE_INPUT_MAX_LEN 74  #define TITLE_INPUT_MAX_LEN 74
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
36    
37  int article_post(SECTION_LIST *p_section)  #define MODIFY_DT_MAX_LEN 50
38    
39    int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new)
40  {  {
41            MYSQL *db = NULL;
42            MYSQL_RES *rs = NULL;
43            MYSQL_ROW row;
44            char sql[SQL_BUFFER_LEN];
45            char *sql_content = NULL;
46          EDITOR_DATA *p_editor_data = NULL;          EDITOR_DATA *p_editor_data = NULL;
         char title[BBS_article_title_max_len + 1];  
47          char title_input[TITLE_INPUT_MAX_LEN + 1];          char title_input[TITLE_INPUT_MAX_LEN + 1];
48            char title_f[BBS_article_title_max_len * 2 + 1];
49            char *content = NULL;
50            char *content_f = NULL;
51            long len_content;
52            char nickname_f[BBS_nickname_max_len * 2 + 1];
53          int sign_id = 0;          int sign_id = 0;
54          long len;          long len;
55          int ch;          int ch;
56          char *p, *q;          char *p, *q;
57            long ret = 0;
58    
59          if (p_section == NULL)          if (p_section == NULL || p_article_new == NULL)
60          {          {
61                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
62          }          }
63    
64          title[0] = '\0';          if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
65            {
66                    clearscr();
67                    moveto(1, 1);
68                    prints("您没有权限在本版块发表文章\n");
69                    press_any_key();
70    
71                    return 0;
72            }
73    
74            p_article_new->title[0] = '\0';
75          title_input[0] = '\0';          title_input[0] = '\0';
76            p_article_new->transship = 0;
77    
78          p_editor_data = editor_data_load("");          p_editor_data = editor_data_load("");
79          if (p_editor_data == NULL)          if (p_editor_data == NULL)
80          {          {
81                  log_error("editor_data_load() error\n");                  log_error("editor_data_load() error\n");
82                  return -2;                  ret = -1;
83                    goto cleanup;
84          }          }
85    
86          // Set title and sign          // Set title and sign
# Line 61  int article_post(SECTION_LIST *p_section Line 88  int article_post(SECTION_LIST *p_section
88          {          {
89                  clearscr();                  clearscr();
90                  moveto(21, 1);                  moveto(21, 1);
91                  prints("发表文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);                  prints("发表文章于 %s[%s] 讨论区,类型: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"));
92                  moveto(22, 1);                  moveto(22, 1);
93                  prints("标题: %s", (title[0] == '\0' ? "[无]" : title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
94                  moveto(23, 1);                  moveto(23, 1);
95                  prints("使用第 %d 个签名", sign_id);                  prints("使用第 %d 个签名", sign_id);
96    
# Line 72  int article_post(SECTION_LIST *p_section Line 99  int article_post(SECTION_LIST *p_section
99                          prints("    按0~3选签名档(0表示不使用)");                          prints("    按0~3选签名档(0表示不使用)");
100    
101                          moveto(24, 1);                          moveto(24, 1);
102                          prints("T改标题, C取消, Enter继续: ");                          prints("T改标题, C取消, Z设为转载, Y设为原创, Enter继续: ");
103                          iflush();                          iflush();
104                          ch = 0;                          ch = 0;
105                  }                  }
# Line 81  int article_post(SECTION_LIST *p_section Line 108  int article_post(SECTION_LIST *p_section
108                  {                  {
109                          switch (toupper(ch))                          switch (toupper(ch))
110                          {                          {
111                            case KEY_NULL:
112                            case KEY_TIMEOUT:
113                                    goto cleanup;
114                          case CR:                          case CR:
115                                  igetch_reset();                                  igetch_reset();
116                                  break;                                  break;
# Line 96  int article_post(SECTION_LIST *p_section Line 126  int article_post(SECTION_LIST *p_section
126                                  len = q - p;                                  len = q - p;
127                                  if (*p != '\0')                                  if (*p != '\0')
128                                  {                                  {
129                                          memcpy(title, p, (size_t)len + 1);                                          memcpy(p_article_new->title, p, (size_t)len + 1);
130                                          memcpy(title_input, title, (size_t)len + 1);                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);
131                                  }                                  }
132                                  ch = 0;                                  ch = 0;
133                                  break;                                  break;
# Line 107  int article_post(SECTION_LIST *p_section Line 137  int article_post(SECTION_LIST *p_section
137                                  prints("取消...");                                  prints("取消...");
138                                  press_any_key();                                  press_any_key();
139                                  goto cleanup;                                  goto cleanup;
140                            case 'Y':
141                                    p_article_new->transship = 0;
142                                    break;
143                            case 'Z':
144                                    p_article_new->transship = 1;
145                                    break;
146                          case '0':                          case '0':
147                          case '1':                          case '1':
148                          case '2':                          case '2':
# Line 120  int article_post(SECTION_LIST *p_section Line 156  int article_post(SECTION_LIST *p_section
156                          break;                          break;
157                  }                  }
158    
159                  if (ch != CR || title[0] == '\0')                  if (ch != CR || p_article_new->title[0] == '\0')
160                  {                  {
161                          continue;                          continue;
162                  }                  }
# Line 138  int article_post(SECTION_LIST *p_section Line 174  int article_post(SECTION_LIST *p_section
174                          {                          {
175                                  switch (toupper(ch))                                  switch (toupper(ch))
176                                  {                                  {
177                                    case KEY_NULL:
178                                    case KEY_TIMEOUT:
179                                            goto cleanup;
180                                  case CR:                                  case CR:
181                                          igetch_reset();                                          igetch_reset();
182                                  case 'S':                                  case 'S':
# Line 166  int article_post(SECTION_LIST *p_section Line 205  int article_post(SECTION_LIST *p_section
205                  }                  }
206          }          }
207    
208          // editor_data_save(p_editor_data, p_data_new, data_new_len);          if (SYS_server_exit) // Do not save data on shutdown
209          log_common("Debug: post article\n");          {
210                    goto cleanup;
211            }
212    
213            content = malloc(ARTICLE_CONTENT_MAX_LEN);
214            if (content == NULL)
215            {
216                    log_error("malloc(content) error: OOM\n");
217                    ret = -1;
218                    goto cleanup;
219            }
220    
221            len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN);
222            if (len_content < 0)
223            {
224                    log_error("editor_data_save() error\n");
225                    ret = -1;
226                    goto cleanup;
227            }
228    
229            db = db_open();
230            if (db == NULL)
231            {
232                    log_error("db_open() error: %s\n", mysql_error(db));
233                    ret = -1;
234                    goto cleanup;
235            }
236    
237            if (sign_id > 0)
238            {
239                    snprintf(sql, sizeof(sql),
240                                     "SELECT sign_%d AS sign FROM user_pubinfo WHERE UID = %d",
241                                     sign_id, BBS_priv.uid);
242    
243                    if (mysql_query(db, sql) != 0)
244                    {
245                            log_error("Query sign error: %s\n", mysql_error(db));
246                            ret = -1;
247                            goto cleanup;
248                    }
249                    if ((rs = mysql_use_result(db)) == NULL)
250                    {
251                            log_error("Get sign data failed\n");
252                            ret = -1;
253                            goto cleanup;
254                    }
255    
256                    if ((row = mysql_fetch_row(rs)))
257                    {
258                            len_content += snprintf(content + len_content,
259                                                                            ARTICLE_CONTENT_MAX_LEN - (size_t)len_content,
260                                                                            "\n\n--\n%s\n", row[0]);
261                    }
262                    mysql_free_result(rs);
263                    rs = NULL;
264            }
265    
266            // Begin transaction
267            if (mysql_query(db, "SET autocommit=0") != 0)
268            {
269                    log_error("SET autocommit=0 error: %s\n", mysql_error(db));
270                    ret = -1;
271                    goto cleanup;
272            }
273    
274            if (mysql_query(db, "BEGIN") != 0)
275            {
276                    log_error("Begin transaction error: %s\n", mysql_error(db));
277                    ret = -1;
278                    goto cleanup;
279            }
280    
281            // Secure SQL parameters
282            content_f = malloc((size_t)len_content * 2 + 1);
283            if (content_f == NULL)
284            {
285                    log_error("malloc(content_f) error: OOM\n");
286                    ret = -1;
287                    goto cleanup;
288            }
289    
290            mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
291            mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
292            mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
293    
294            free(content);
295            content = NULL;
296    
297            // Add content
298            sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
299            if (sql_content == NULL)
300            {
301                    log_error("malloc(sql_content) error: OOM\n");
302                    ret = -1;
303                    goto cleanup;
304            }
305    
306            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
307                             "INSERT INTO bbs_content(AID, content) values(0, '%s')",
308                             content_f);
309    
310            free(content_f);
311            content_f = NULL;
312    
313            if (mysql_query(db, sql_content) != 0)
314            {
315                    log_error("Add article content error: %s\n", mysql_error(db));
316                    ret = -1;
317                    goto cleanup;
318            }
319    
320            p_article_new->cid = (int32_t)mysql_insert_id(db);
321    
322            free(sql_content);
323            sql_content = NULL;
324    
325            // Add article
326            snprintf(sql, sizeof(sql),
327                             "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
328                             "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
329                             "VALUES(%d, 0, %d, '%s', '%s', '%s', %d, %d, NOW(), '%s', 1, %d, NOW(), 1, %ld)",
330                             p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,
331                             p_article_new->cid, p_article_new->transship, hostaddr_client, BBS_user_exp, len_content);
332    
333            if (mysql_query(db, sql) != 0)
334            {
335                    log_error("Add article error: %s\n", mysql_error(db));
336                    ret = -1;
337                    goto cleanup;
338            }
339    
340            p_article_new->aid = (int32_t)mysql_insert_id(db);
341    
342            // Link content to article
343            snprintf(sql, sizeof(sql),
344                             "UPDATE bbs_content SET AID = %d WHERE CID = %d",
345                             p_article_new->aid, p_article_new->cid);
346    
347            if (mysql_query(db, sql) != 0)
348            {
349                    log_error("Update content error: %s\n", mysql_error(db));
350                    ret = -1;
351                    goto cleanup;
352            }
353    
354            // Add exp
355            if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
356            {
357                    snprintf(sql, sizeof(sql),
358                                     "UPDATE user_pubinfo SET exp = exp + %d WHERE UID = %d",
359                                     (p_article_new->transship ? 5 : 15), BBS_priv.uid);
360    
361                    if (mysql_query(db, sql) != 0)
362                    {
363                            log_error("Update exp error: %s\n", mysql_error(db));
364                            ret = -1;
365                            goto cleanup;
366                    }
367            }
368    
369            // Add log
370            snprintf(sql, sizeof(sql),
371                             "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
372                             "VALUES(%d, %d, 'A', NOW(), '%s')",
373                             p_article_new->aid, BBS_priv.uid, hostaddr_client);
374    
375            if (mysql_query(db, sql) != 0)
376            {
377                    log_error("Add log error: %s\n", mysql_error(db));
378                    ret = -1;
379                    goto cleanup;
380            }
381    
382            // Commit transaction
383            if (mysql_query(db, "COMMIT") != 0)
384            {
385                    log_error("Commit transaction error: %s\n", mysql_error(db));
386                    ret = -1;
387                    goto cleanup;
388            }
389    
390            mysql_close(db);
391            db = NULL;
392    
393            clearscr();
394            moveto(1, 1);
395            prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
396            press_any_key();
397            ret = 1; // Success
398    
399  cleanup:  cleanup:
400            mysql_close(db);
401    
402            // Cleanup buffers
403          editor_data_cleanup(p_editor_data);          editor_data_cleanup(p_editor_data);
404    
405          return 0;          free(sql_content);
406            free(content);
407            free(content_f);
408    
409            return (int)ret;
410  }  }
411    
412  int article_modify(SECTION_LIST *p_section, ARTICLE *p_article)  int article_modify(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
413  {  {
414          MYSQL *db;          MYSQL *db = NULL;
415          MYSQL_RES *rs;          MYSQL_RES *rs = NULL;
416          MYSQL_ROW row;          MYSQL_ROW row;
417          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
418            char *sql_content = NULL;
419            char *content = NULL;
420            char *content_f = NULL;
421            long len_content;
422          int ch;          int ch;
423            long ret = 0;
424            time_t now;
425            struct tm tm_modify_dt;
426            char str_modify_dt[MODIFY_DT_MAX_LEN + 1];
427    
428          EDITOR_DATA *p_editor_data = NULL;          EDITOR_DATA *p_editor_data = NULL;
429    
# Line 190  int article_modify(SECTION_LIST *p_secti Line 432  int article_modify(SECTION_LIST *p_secti
432                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
433          }          }
434    
435            if (p_article->excerption) // Modify is not allowed
436            {
437                    clearscr();
438                    moveto(1, 1);
439                    prints("该文章无法被编辑,请联系版主。");
440                    press_any_key();
441    
442                    return 0;
443            }
444    
445          db = db_open();          db = db_open();
446          if (db == NULL)          if (db == NULL)
447          {          {
448                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
449                  return -1;                  ret = -1;
450                    goto cleanup;
451          }          }
452    
453          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 206  int article_modify(SECTION_LIST *p_secti Line 459  int article_modify(SECTION_LIST *p_secti
459          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
460          {          {
461                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
462                  return -2;                  ret = -1;
463                    goto cleanup;
464          }          }
465          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
466          {          {
467                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
468                  return -2;                  ret = -1;
469                    goto cleanup;
470          }          }
471    
472          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
473          {          {
474                  p_editor_data = editor_data_load(row[1]);                  content = malloc(ARTICLE_CONTENT_MAX_LEN);
475                    if (content == NULL)
476                    {
477                            log_error("malloc(content) error: OOM\n");
478                            ret = -1;
479                            goto cleanup;
480                    }
481    
482                    strncpy(content, row[1], ARTICLE_CONTENT_MAX_LEN - 1);
483                    content[ARTICLE_CONTENT_MAX_LEN - 1] = '\0';
484    
485                    // Remove control sequence
486                    len_content = str_filter(content, 0);
487    
488                    p_editor_data = editor_data_load(content);
489                  if (p_editor_data == NULL)                  if (p_editor_data == NULL)
490                  {                  {
491                          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]));
492                          mysql_free_result(rs);                          ret = -1;
493                          mysql_close(db);                          goto cleanup;
                         return -3;  
494                  }                  }
495    
496                    free(content);
497                    content = NULL;
498          }          }
499          mysql_free_result(rs);          mysql_free_result(rs);
500            rs = NULL;
501    
502          mysql_close(db);          mysql_close(db);
503            db = NULL;
504    
505          for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)          for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
506          {          {
# Line 241  int article_modify(SECTION_LIST *p_secti Line 515  int article_modify(SECTION_LIST *p_secti
515                  {                  {
516                          switch (toupper(ch))                          switch (toupper(ch))
517                          {                          {
518                            case KEY_NULL:
519                            case KEY_TIMEOUT:
520                                    goto cleanup;
521                          case CR:                          case CR:
522                                  igetch_reset();                                  igetch_reset();
523                          case 'S':                          case 'S':
# Line 261  int article_modify(SECTION_LIST *p_secti Line 538  int article_modify(SECTION_LIST *p_secti
538                  }                  }
539          }          }
540    
541          // editor_data_save(p_editor_data, p_data_new, data_new_len);          if (SYS_server_exit) // Do not save data on shutdown
542          log_common("Debug: modify article\n");          {
543                    goto cleanup;
544            }
545    
546            // Allocate buffers in big size
547            content = malloc(ARTICLE_CONTENT_MAX_LEN);
548            if (content == NULL)
549            {
550                    log_error("malloc(content) error: OOM\n");
551                    ret = -1;
552                    goto cleanup;
553            }
554    
555            len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN - LINE_BUFFER_LEN);
556            if (len_content < 0)
557            {
558                    log_error("editor_data_save() error\n");
559                    ret = -1;
560                    goto cleanup;
561            }
562    
563            time(&now);
564            localtime_r(&now, &tm_modify_dt);
565            strftime(str_modify_dt, sizeof(str_modify_dt), "%Y-%m-%d %H:%M:%S (UTC %z)", &tm_modify_dt);
566    
567            len_content += snprintf(content + len_content, LINE_BUFFER_LEN,
568                                                            "\n--\n※ 作者已于 %s 修改本文※\n",
569                                                            str_modify_dt);
570    
571            db = db_open();
572            if (db == NULL)
573            {
574                    log_error("db_open() error: %s\n", mysql_error(db));
575                    ret = -1;
576                    goto cleanup;
577            }
578    
579            // Begin transaction
580            if (mysql_query(db, "SET autocommit=0") != 0)
581            {
582                    log_error("SET autocommit=0 error: %s\n", mysql_error(db));
583                    ret = -1;
584                    goto cleanup;
585            }
586    
587            if (mysql_query(db, "BEGIN") != 0)
588            {
589                    log_error("Begin transaction error: %s\n", mysql_error(db));
590                    ret = -1;
591                    goto cleanup;
592            }
593    
594            // Secure SQL parameters
595            content_f = malloc((size_t)len_content * 2 + 1);
596            if (content_f == NULL)
597            {
598                    log_error("malloc(content_f) error: OOM\n");
599                    ret = -1;
600                    goto cleanup;
601            }
602    
603            mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
604    
605            free(content);
606            content = NULL;
607    
608            // Add content
609            sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
610            if (sql_content == NULL)
611            {
612                    log_error("malloc(sql_content) error: OOM\n");
613                    ret = -1;
614                    goto cleanup;
615            }
616    
617            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
618                             "INSERT INTO bbs_content(AID, content) values(%d, '%s')",
619                             p_article->aid, content_f);
620    
621            free(content_f);
622            content_f = NULL;
623    
624            if (mysql_query(db, sql_content) != 0)
625            {
626                    log_error("Add article content error: %s\n", mysql_error(db));
627                    ret = -1;
628                    goto cleanup;
629            }
630    
631            p_article_new->cid = (int32_t)mysql_insert_id(db);
632    
633            free(sql_content);
634            sql_content = NULL;
635    
636            // Update article
637            snprintf(sql, sizeof(sql),
638                             "UPDATE bbs SET CID = %d, length = %ld, excerption = 0 WHERE AID = %d", // Set excerption = 0 explictly in case of rare condition
639                             p_article_new->cid, len_content, p_article->aid);
640    
641            if (mysql_query(db, sql) != 0)
642            {
643                    log_error("Add article error: %s\n", mysql_error(db));
644                    ret = -1;
645                    goto cleanup;
646            }
647    
648            if (mysql_query(db, sql) != 0)
649            {
650                    log_error("Update content error: %s\n", mysql_error(db));
651                    ret = -1;
652                    goto cleanup;
653            }
654    
655            // Add log
656            snprintf(sql, sizeof(sql),
657                             "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
658                             "VALUES(%d, %d, 'M', NOW(), '%s')",
659                             p_article->aid, BBS_priv.uid, hostaddr_client);
660    
661            if (mysql_query(db, sql) != 0)
662            {
663                    log_error("Add log error: %s\n", mysql_error(db));
664                    ret = -1;
665                    goto cleanup;
666            }
667    
668            // Commit transaction
669            if (mysql_query(db, "COMMIT") != 0)
670            {
671                    log_error("Commit transaction error: %s\n", mysql_error(db));
672                    ret = -1;
673                    goto cleanup;
674            }
675    
676            mysql_close(db);
677            db = NULL;
678    
679            clearscr();
680            moveto(1, 1);
681            prints("修改完成,新内容通常会在%d秒后可见", BBS_section_list_load_interval);
682            press_any_key();
683            ret = 1; // Success
684    
685  cleanup:  cleanup:
686            mysql_free_result(rs);
687            mysql_close(db);
688    
689            // Cleanup buffers
690          editor_data_cleanup(p_editor_data);          editor_data_cleanup(p_editor_data);
691    
692          return 0;          free(sql_content);
693            free(content);
694            free(content_f);
695    
696            return (int)ret;
697  }  }
698    
699  int article_reply(SECTION_LIST *p_section, ARTICLE *p_article)  int article_reply(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
700  {  {
701          MYSQL *db;          MYSQL *db = NULL;
702          MYSQL_RES *rs;          MYSQL_RES *rs = NULL;
703          MYSQL_ROW row;          MYSQL_ROW row;
         char sql[SQL_BUFFER_LEN];  
         char content[LINE_BUFFER_LEN * ARTICLE_QUOTE_MAX_LINES + 1];  
         char content_f[ARTICLE_CONTENT_MAX_LEN];  
704          long line_offsets[ARTICLE_QUOTE_MAX_LINES + 1];          long line_offsets[ARTICLE_QUOTE_MAX_LINES + 1];
705            char sql[SQL_BUFFER_LEN];
706            char *sql_content = NULL;
707          EDITOR_DATA *p_editor_data = NULL;          EDITOR_DATA *p_editor_data = NULL;
708          char title[BBS_article_title_max_len + 1];          char title_input[BBS_article_title_max_len + sizeof("Re: ")];
709          char title_input[BBS_article_title_max_len + 1 + 4]; // + "Re: "          char title_f[BBS_article_title_max_len * 2 + 1];
710            char *content = NULL;
711            char *content_f = NULL;
712            long len_content;
713            char nickname_f[BBS_nickname_max_len * 2 + 1];
714          int sign_id = 0;          int sign_id = 0;
715          long len;          long len;
716          int ch;          int ch;
# Line 290  int article_reply(SECTION_LIST *p_sectio Line 719  int article_reply(SECTION_LIST *p_sectio
719          int display_len;          int display_len;
720          long quote_content_lines;          long quote_content_lines;
721          long i;          long i;
722            long ret = 0;
723            int topic_locked = 0;
724    
725          if (p_section == NULL || p_article == NULL)          if (p_section == NULL || p_article == NULL)
726          {          {
727                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
728          }          }
729    
730          title[0] = '\0';          if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
731            {
732                    clearscr();
733                    moveto(1, 1);
734                    prints("您没有权限在本版块发表文章\n");
735                    press_any_key();
736    
737                    return 0;
738            }
739    
740            p_article_new->title[0] = '\0';
741          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);          snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
742          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);
743          title_input[len] = '\0';          title_input[len] = '\0';
744    
745          db = db_open();          db = db_open();
746          if (db == NULL)          if (db == NULL)
747          {          {
748                  log_error("db_open() error: %s\n", mysql_error(db));                  log_error("db_open() error: %s\n", mysql_error(db));
749                  return -1;                  ret = -1;
750                    goto cleanup;
751            }
752    
753            snprintf(sql, sizeof(sql),
754                             "SELECT `lock` FROM bbs WHERE AID = %d",
755                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
756    
757            if (mysql_query(db, sql) != 0)
758            {
759                    log_error("Query article status error: %s\n", mysql_error(db));
760                    ret = -1;
761                    goto cleanup;
762            }
763            if ((rs = mysql_store_result(db)) == NULL)
764            {
765                    log_error("Get article status data failed\n");
766                    ret = -1;
767                    goto cleanup;
768            }
769    
770            if ((row = mysql_fetch_row(rs)))
771            {
772                    if (atoi(row[0]) != 0)
773                    {
774                            topic_locked = 1;
775                    }
776            }
777            mysql_free_result(rs);
778            rs = NULL;
779    
780            if (topic_locked) // Reply is not allowed
781            {
782                    mysql_close(db);
783                    db = NULL;
784    
785                    clearscr();
786                    moveto(1, 1);
787                    prints("该主题谢绝回复");
788                    press_any_key();
789    
790                    goto cleanup;
791          }          }
792    
793          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
# Line 317  int article_reply(SECTION_LIST *p_sectio Line 799  int article_reply(SECTION_LIST *p_sectio
799          if (mysql_query(db, sql) != 0)          if (mysql_query(db, sql) != 0)
800          {          {
801                  log_error("Query article content error: %s\n", mysql_error(db));                  log_error("Query article content error: %s\n", mysql_error(db));
802                  return -2;                  ret = -1;
803                    goto cleanup;
804          }          }
805          if ((rs = mysql_use_result(db)) == NULL)          if ((rs = mysql_use_result(db)) == NULL)
806          {          {
807                  log_error("Get article content data failed\n");                  log_error("Get article content data failed\n");
808                  return -2;                  ret = -1;
809                    goto cleanup;
810          }          }
811    
812          if ((row = mysql_fetch_row(rs)))          if ((row = mysql_fetch_row(rs)))
813          {          {
814                    content = malloc(ARTICLE_CONTENT_MAX_LEN);
815                    if (content == NULL)
816                    {
817                            log_error("malloc(content) error: OOM\n");
818                            ret = -1;
819                            goto cleanup;
820                    }
821    
822                    content_f = malloc(ARTICLE_CONTENT_MAX_LEN);
823                    if (content_f == NULL)
824                    {
825                            log_error("malloc(content_f) error: OOM\n");
826                            ret = -1;
827                            goto cleanup;
828                    }
829    
830                  // Apply LML render to content body                  // Apply LML render to content body
831                  len = lml_plain(row[1], content_f, ARTICLE_CONTENT_MAX_LEN);                  len = lml_plain(row[1], content_f, ARTICLE_CONTENT_MAX_LEN);
832                  content_f[len] = '\0';                  content_f[len] = '\0';
833    
834                  // Remove control sequence                  // Remove control sequence
835                  len = ctrl_seq_filter(content_f);                  len = str_filter(content_f, 0);
836    
837                  len = snprintf(content, sizeof(content), "\n\n【 在 %s (%s) 的大作中提到: 】\n", p_article->username, p_article->nickname);                  len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
838                                               "\n\n【 在 %s (%s) 的大作中提到: 】\n",
839                                               p_article->username, p_article->nickname);
840    
841                  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);
842                  for (i = 0; i < quote_content_lines; i++)                  for (i = 0; i < quote_content_lines; i++)
843                  {                  {
844                          memcpy(content + len, ": ", 2); // quote line prefix                          memcpy(content + len, ": ", 2); // quote line prefix
845                          len += 2;                          len += 2;
846                          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]));
847                          len += (line_offsets[i + 1] - line_offsets[i]);                          len += (line_offsets[i + 1] - line_offsets[i]);
848                            if (content[len - 1] != '\n') // Appennd \n if not exist
849                            {
850                                    content[len] = '\n';
851                                    len++;
852                            }
853                    }
854                    if (content[len - 1] != '\n') // Appennd \n if not exist
855                    {
856                            content[len] = '\n';
857                            len++;
858                  }                  }
859                  content[len] = '\0';                  content[len] = '\0';
860    
861                    free(content_f);
862                    content_f = NULL;
863    
864                    p_editor_data = editor_data_load(content);
865                    if (p_editor_data == NULL)
866                    {
867                            log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
868                            ret = -1;
869                            goto cleanup;
870                    }
871    
872                    free(content);
873                    content = NULL;
874          }          }
875          mysql_free_result(rs);          mysql_free_result(rs);
876          mysql_close(db);          rs = NULL;
877    
878          p_editor_data = editor_data_load(content);          mysql_close(db);
879          if (p_editor_data == NULL)          db = NULL;
         {  
                 log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));  
                 return -3;  
         }  
880    
881          // Set title and sign          // Set title and sign
882          for (ch = 'T'; !SYS_server_exit;)          for (ch = 'T'; !SYS_server_exit;)
# Line 363  int article_reply(SECTION_LIST *p_sectio Line 885  int article_reply(SECTION_LIST *p_sectio
885                  moveto(21, 1);                  moveto(21, 1);
886                  prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);                  prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);
887                  moveto(22, 1);                  moveto(22, 1);
888                  prints("标题: %s", (title[0] == '\0' ? "[无]" : title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
889                  moveto(23, 1);                  moveto(23, 1);
890                  prints("使用第 %d 个签名", sign_id);                  prints("使用第 %d 个签名", sign_id);
891    
# Line 381  int article_reply(SECTION_LIST *p_sectio Line 903  int article_reply(SECTION_LIST *p_sectio
903                  {                  {
904                          switch (toupper(ch))                          switch (toupper(ch))
905                          {                          {
906                            case KEY_NULL:
907                            case KEY_TIMEOUT:
908                                    goto cleanup;
909                          case CR:                          case CR:
910                                  igetch_reset();                                  igetch_reset();
911                                  break;                                  break;
# Line 396  int article_reply(SECTION_LIST *p_sectio Line 921  int article_reply(SECTION_LIST *p_sectio
921                                  len = q - p;                                  len = q - p;
922                                  if (*p != '\0')                                  if (*p != '\0')
923                                  {                                  {
924                                          memcpy(title, p, (size_t)len + 1);                                          memcpy(p_article_new->title, p, (size_t)len + 1);
925                                          memcpy(title_input, title, (size_t)len + 1);                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);
926                                  }                                  }
927                                  ch = 0;                                  ch = 0;
928                                  break;                                  break;
# Line 420  int article_reply(SECTION_LIST *p_sectio Line 945  int article_reply(SECTION_LIST *p_sectio
945                          break;                          break;
946                  }                  }
947    
948                  if (ch != CR || title[0] == '\0')                  if (ch != CR || p_article_new->title[0] == '\0')
949                  {                  {
950                          continue;                          continue;
951                  }                  }
# Line 438  int article_reply(SECTION_LIST *p_sectio Line 963  int article_reply(SECTION_LIST *p_sectio
963                          {                          {
964                                  switch (toupper(ch))                                  switch (toupper(ch))
965                                  {                                  {
966                                    case KEY_NULL:
967                                    case KEY_TIMEOUT:
968                                            goto cleanup;
969                                  case CR:                                  case CR:
970                                          igetch_reset();                                          igetch_reset();
971                                  case 'S':                                  case 'S':
# Line 466  int article_reply(SECTION_LIST *p_sectio Line 994  int article_reply(SECTION_LIST *p_sectio
994                  }                  }
995          }          }
996    
997          // editor_data_save(p_editor_data, p_data_new, data_new_len);          if (SYS_server_exit) // Do not save data on shutdown
998          log_common("Debug: reply article\n");          {
999                    goto cleanup;
1000            }
1001    
1002            content = malloc(ARTICLE_CONTENT_MAX_LEN);
1003            if (content == NULL)
1004            {
1005                    log_error("malloc(content) error: OOM\n");
1006                    ret = -1;
1007                    goto cleanup;
1008            }
1009    
1010            len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN);
1011            if (len_content < 0)
1012            {
1013                    log_error("editor_data_save() error\n");
1014                    ret = -1;
1015                    goto cleanup;
1016            }
1017    
1018            db = db_open();
1019            if (db == NULL)
1020            {
1021                    log_error("db_open() error: %s\n", mysql_error(db));
1022                    ret = -1;
1023                    goto cleanup;
1024            }
1025    
1026            if (sign_id > 0)
1027            {
1028                    snprintf(sql, sizeof(sql),
1029                                     "SELECT sign_%d AS sign FROM user_pubinfo WHERE UID = %d",
1030                                     sign_id, BBS_priv.uid);
1031    
1032                    if (mysql_query(db, sql) != 0)
1033                    {
1034                            log_error("Query sign error: %s\n", mysql_error(db));
1035                            ret = -1;
1036                            goto cleanup;
1037                    }
1038                    if ((rs = mysql_use_result(db)) == NULL)
1039                    {
1040                            log_error("Get sign data failed\n");
1041                            ret = -1;
1042                            goto cleanup;
1043                    }
1044    
1045                    if ((row = mysql_fetch_row(rs)))
1046                    {
1047                            len_content += snprintf(content + len_content,
1048                                                                            ARTICLE_CONTENT_MAX_LEN - (size_t)len_content,
1049                                                                            "\n\n--\n%s\n", row[0]);
1050                    }
1051                    mysql_free_result(rs);
1052                    rs = NULL;
1053            }
1054    
1055            // Begin transaction
1056            if (mysql_query(db, "SET autocommit=0") != 0)
1057            {
1058                    log_error("SET autocommit=0 error: %s\n", mysql_error(db));
1059                    ret = -1;
1060                    goto cleanup;
1061            }
1062    
1063            if (mysql_query(db, "BEGIN") != 0)
1064            {
1065                    log_error("Begin transaction error: %s\n", mysql_error(db));
1066                    ret = -1;
1067                    goto cleanup;
1068            }
1069    
1070            // Secure SQL parameters
1071            content_f = malloc((size_t)len_content * 2 + 1);
1072            if (content_f == NULL)
1073            {
1074                    log_error("malloc(content_f) error: OOM\n");
1075                    ret = -1;
1076                    goto cleanup;
1077            }
1078    
1079            mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
1080            mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
1081            mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
1082    
1083            free(content);
1084            content = NULL;
1085    
1086            // Add content
1087            sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
1088            if (sql_content == NULL)
1089            {
1090                    log_error("malloc(sql_content) error: OOM\n");
1091                    ret = -1;
1092                    goto cleanup;
1093            }
1094    
1095            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
1096                             "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1097                             content_f);
1098    
1099            free(content_f);
1100            content_f = NULL;
1101    
1102            if (mysql_query(db, sql_content) != 0)
1103            {
1104                    log_error("Add article content error: %s\n", mysql_error(db));
1105                    ret = -1;
1106                    goto cleanup;
1107            }
1108    
1109            p_article_new->cid = (int32_t)mysql_insert_id(db);
1110    
1111            free(sql_content);
1112            sql_content = NULL;
1113    
1114            // Add article
1115            snprintf(sql, sizeof(sql),
1116                             "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
1117                             "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
1118                             "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %ld)",
1119                             p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1120                             BBS_priv.uid, BBS_username, nickname_f, title_f,
1121                             p_article_new->cid, hostaddr_client, BBS_user_exp, len_content);
1122    
1123            if (mysql_query(db, sql) != 0)
1124            {
1125                    log_error("Add article error: %s\n", mysql_error(db));
1126                    ret = -1;
1127                    goto cleanup;
1128            }
1129    
1130            p_article_new->aid = (int32_t)mysql_insert_id(db);
1131    
1132            // Update topic article
1133            snprintf(sql, sizeof(sql),
1134                             "UPDATE bbs SET reply_count = reply_count + 1, "
1135                             "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "
1136                             "last_reply_nickname = '%s' WHERE AID = %d",
1137                             BBS_priv.uid, BBS_username, nickname_f,
1138                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
1139    
1140            if (mysql_query(db, sql) != 0)
1141            {
1142                    log_error("Update topic article error: %s\n", mysql_error(db));
1143                    ret = -1;
1144                    goto cleanup;
1145            }
1146    
1147            // Link content to article
1148            snprintf(sql, sizeof(sql),
1149                             "UPDATE bbs_content SET AID = %d WHERE CID = %d",
1150                             p_article_new->aid, p_article_new->cid);
1151    
1152            if (mysql_query(db, sql) != 0)
1153            {
1154                    log_error("Update content error: %s\n", mysql_error(db));
1155                    ret = -1;
1156                    goto cleanup;
1157            }
1158    
1159            // Add exp
1160            if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
1161            {
1162                    snprintf(sql, sizeof(sql),
1163                                     "UPDATE user_pubinfo SET exp = exp + %d WHERE UID = %d",
1164                                     3, BBS_priv.uid);
1165    
1166                    if (mysql_query(db, sql) != 0)
1167                    {
1168                            log_error("Update exp error: %s\n", mysql_error(db));
1169                            ret = -1;
1170                            goto cleanup;
1171                    }
1172            }
1173    
1174            // Add log
1175            snprintf(sql, sizeof(sql),
1176                             "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
1177                             "VALUES(%d, %d, 'A', NOW(), '%s')",
1178                             p_article_new->aid, BBS_priv.uid, hostaddr_client);
1179    
1180            if (mysql_query(db, sql) != 0)
1181            {
1182                    log_error("Add log error: %s\n", mysql_error(db));
1183                    ret = -1;
1184                    goto cleanup;
1185            }
1186    
1187            // Commit transaction
1188            if (mysql_query(db, "COMMIT") != 0)
1189            {
1190                    log_error("Commit transaction error: %s\n", mysql_error(db));
1191                    ret = -1;
1192                    goto cleanup;
1193            }
1194    
1195            mysql_close(db);
1196            db = NULL;
1197    
1198            clearscr();
1199            moveto(1, 1);
1200            prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1201            press_any_key();
1202            ret = 1; // Success
1203    
1204  cleanup:  cleanup:
1205            mysql_free_result(rs);
1206            mysql_close(db);
1207    
1208            // Cleanup buffers
1209          editor_data_cleanup(p_editor_data);          editor_data_cleanup(p_editor_data);
1210    
1211          return 0;          free(sql_content);
1212            free(content);
1213            free(content_f);
1214    
1215            return (int)ret;
1216  }  }


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

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