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


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

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