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


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

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