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


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

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