/[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.1 by sysadm, Thu Jun 12 12:14:28 2025 UTC Revision 1.15 by sysadm, Sun Jun 15 02:24:45 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"
26    #include "lml.h"
27    #include "database.h"
28    #include "user_priv.h"
29    #include <ctype.h>
30    #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 MODIFY_DT_MAX_LEN 50
40    
41  int article_post(SECTION_LIST *p_section, ARTICLE *p_article, ARTICLE_POST_TYPE type)  int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new)
42  {  {
43          log_common("Debug: sid=%d aid=%d type=%d\n",          MYSQL *db = NULL;
44                             p_section->sid, (p_article == NULL ? 0 : p_article->aid), type);          MYSQL_RES *rs = NULL;
45            MYSQL_ROW row;
46            char sql[SQL_BUFFER_LEN];
47            char *sql_content = NULL;
48            EDITOR_DATA *p_editor_data = NULL;
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;
56            long len;
57            int ch;
58            char *p, *q;
59            long ret = 0;
60    
61            if (p_section == NULL || p_article_new == NULL)
62            {
63                    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("");
81            if (p_editor_data == NULL)
82            {
83                    log_error("editor_data_load() error\n");
84                    return -2;
85            }
86    
87            // Set title and sign
88            for (ch = 'T'; !SYS_server_exit;)
89            {
90                    clearscr();
91                    moveto(21, 1);
92                    prints("发表文章于 %s[%s] 讨论区,类型: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"));
93                    moveto(22, 1);
94                    prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
95                    moveto(23, 1);
96                    prints("使用第 %d 个签名", sign_id);
97    
98                    if (toupper(ch) != 'T')
99                    {
100                            prints("    按0~3选签名档(0表示不使用)");
101    
102                            moveto(24, 1);
103                            prints("T改标题, C取消, Z设为转载, Y设为原创, Enter继续: ");
104                            iflush();
105                            ch = 0;
106                    }
107    
108                    for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
109                    {
110                            switch (toupper(ch))
111                            {
112                            case CR:
113                                    igetch_reset();
114                                    break;
115                            case 'T':
116                                    moveto(24, 1);
117                                    clrtoeol();
118                                    len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);
119                                    for (p = title_input; *p == ' '; p++)
120                                            ;
121                                    for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
122                                            ;
123                                    *q = '\0';
124                                    len = q - p;
125                                    if (*p != '\0')
126                                    {
127                                            memcpy(p_article_new->title, p, (size_t)len + 1);
128                                            memcpy(title_input, p_article_new->title, (size_t)len + 1);
129                                    }
130                                    ch = 0;
131                                    break;
132                            case 'C':
133                                    clearscr();
134                                    moveto(1, 1);
135                                    prints("取消...");
136                                    press_any_key();
137                                    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':
145                            case '1':
146                            case '2':
147                            case '3':
148                                    sign_id = ch - '0';
149                                    break;
150                            default: // Invalid selection
151                                    continue;
152                            }
153    
154                            break;
155                    }
156    
157                    if (ch != CR || p_article_new->title[0] == '\0')
158                    {
159                            continue;
160                    }
161    
162                    for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
163                    {
164                            editor_display(p_editor_data);
165    
166                            clearscr();
167                            moveto(1, 1);
168                            prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
169                            iflush();
170    
171                            for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
172                            {
173                                    switch (toupper(ch))
174                                    {
175                                    case CR:
176                                            igetch_reset();
177                                    case 'S':
178                                            break;
179                                    case 'C':
180                                            clearscr();
181                                            moveto(1, 1);
182                                            prints("取消...");
183                                            press_any_key();
184                                            goto cleanup;
185                                    case 'T':
186                                            break;
187                                    case 'E':
188                                            break;
189                                    default: // Invalid selection
190                                            continue;
191                                    }
192    
193                                    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            // Add log
360            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:
387            mysql_close(db);
388    
389            // Cleanup buffers
390            editor_data_cleanup(p_editor_data);
391    
392            free(sql_content);
393            free(content);
394            free(content_f);
395    
396            return (int)ret;
397    }
398    
399    int article_modify(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
400    {
401            MYSQL *db = NULL;
402            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)
418            {
419                    log_error("NULL pointer error\n");
420            }
421    
422            if (p_article->excerption) // Modify is not allowed
423            {
424                    clearscr();
425                    moveto(1, 1);
426                    prints("该文章无法被编辑,请联系版主。");
427                    press_any_key();
428    
429                    return 0;
430            }
431    
432            if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
433            {
434                    clearscr();
435                    moveto(1, 1);
436                    prints("您没有权限在本版块发表文章\n");
437                    press_any_key();
438    
439                    return 0;
440            }
441    
442            db = db_open();
443            if (db == NULL)
444            {
445                    log_error("db_open() error: %s\n", mysql_error(db));
446                    return -1;
447            }
448    
449            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            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, excerption = 0 WHERE AID = %d", // Set excerption = 0 explictly in case of rare condition
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);
676    
677            free(sql_content);
678            free(content);
679            free(content_f);
680    
681            return (int)ret;
682    }
683    
684    int article_reply(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
685    {
686            MYSQL *db = NULL;
687            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            int topic_locked = 0;
709    
710            if (p_section == NULL || p_article == NULL)
711            {
712                    log_error("NULL pointer error\n");
713            }
714    
715            if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
716            {
717                    clearscr();
718                    moveto(1, 1);
719                    prints("您没有权限在本版块发表文章\n");
720                    press_any_key();
721    
722                    return 0;
723            }
724    
725            p_article_new->title[0] = '\0';
726            snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
727            len = split_line(title_input, TITLE_INPUT_MAX_LEN, &eol, &display_len);
728            title_input[len] = '\0';
729    
730            db = db_open();
731            if (db == NULL)
732            {
733                    log_error("db_open() error: %s\n", mysql_error(db));
734                    return -1;
735            }
736    
737            snprintf(sql, sizeof(sql),
738                             "SELECT `lock` FROM bbs WHERE AID = %d",
739                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
740    
741            if (mysql_query(db, sql) != 0)
742            {
743                    log_error("Query article status error: %s\n", mysql_error(db));
744                    return -2;
745            }
746            if ((rs = mysql_store_result(db)) == NULL)
747            {
748                    log_error("Get article status data failed\n");
749                    return -2;
750            }
751    
752            if ((row = mysql_fetch_row(rs)))
753            {
754                    if (atoi(row[0]) != 0)
755                    {
756                            topic_locked = 1;
757                    }
758            }
759            mysql_free_result(rs);
760            rs = NULL;
761    
762            if (topic_locked) // Reply is not allowed
763            {
764                    clearscr();
765                    moveto(1, 1);
766                    prints("该主题谢绝回复");
767                    press_any_key();
768    
769                    goto cleanup;
770            }
771    
772            snprintf(sql, sizeof(sql),
773                             "SELECT bbs_content.CID, bbs_content.content "
774                             "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "
775                             "WHERE bbs.AID = %d",
776                             p_article->aid);
777    
778            if (mysql_query(db, sql) != 0)
779            {
780                    log_error("Query article content error: %s\n", mysql_error(db));
781                    return -2;
782            }
783            if ((rs = mysql_use_result(db)) == NULL)
784            {
785                    log_error("Get article content data failed\n");
786                    return -2;
787            }
788    
789            if ((row = mysql_fetch_row(rs)))
790            {
791                    content = malloc(ARTICLE_CONTENT_MAX_LEN);
792                    if (content == NULL)
793                    {
794                            log_error("malloc(content) error: OOM\n");
795                            ret = -1;
796                            goto cleanup;
797                    }
798    
799                    content_f = malloc(ARTICLE_CONTENT_MAX_LEN);
800                    if (content_f == NULL)
801                    {
802                            log_error("malloc(content_f) error: OOM\n");
803                            ret = -1;
804                            goto cleanup;
805                    }
806    
807                    // Apply LML render to content body
808                    len = lml_plain(row[1], content_f, ARTICLE_CONTENT_MAX_LEN);
809                    content_f[len] = '\0';
810    
811                    // Remove control sequence
812                    len = ctrl_seq_filter(content_f);
813    
814                    len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
815                                               "\n\n【 在 %s (%s) 的大作中提到: 】\n",
816                                               p_article->username, p_article->nickname);
817    
818                    quote_content_lines = split_data_lines(content_f, ARTICLE_QUOTE_LINE_MAX_LEN, line_offsets, ARTICLE_QUOTE_MAX_LINES + 1);
819                    for (i = 0; i < quote_content_lines; i++)
820                    {
821                            memcpy(content + len, ": ", 2); // quote line prefix
822                            len += 2;
823                            memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i]));
824                            len += (line_offsets[i + 1] - line_offsets[i]);
825                    }
826                    if (content[len - 1] != '\n') // Appennd \n if not exist
827                    {
828                            content[len] = '\n';
829                            len++;
830                    }
831                    content[len] = '\0';
832    
833                    free(content_f);
834                    content_f = NULL;
835    
836                    p_editor_data = editor_data_load(content);
837                    if (p_editor_data == NULL)
838                    {
839                            log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
840                            ret = -3;
841                            goto cleanup;
842                    }
843    
844                    free(content);
845                    content = NULL;
846            }
847            mysql_free_result(rs);
848            rs = NULL;
849    
850            mysql_close(db);
851            db = NULL;
852    
853            // Set title and sign
854            for (ch = 'T'; !SYS_server_exit;)
855            {
856                    clearscr();
857                    moveto(21, 1);
858                    prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);
859                    moveto(22, 1);
860                    prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
861                    moveto(23, 1);
862                    prints("使用第 %d 个签名", sign_id);
863    
864                    if (toupper(ch) != 'T')
865                    {
866                            prints("    按0~3选签名档(0表示不使用)");
867    
868                            moveto(24, 1);
869                            prints("T改标题, C取消, Enter继续: ");
870                            iflush();
871                            ch = 0;
872                    }
873    
874                    for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
875                    {
876                            switch (toupper(ch))
877                            {
878                            case CR:
879                                    igetch_reset();
880                                    break;
881                            case 'T':
882                                    moveto(24, 1);
883                                    clrtoeol();
884                                    len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);
885                                    for (p = title_input; *p == ' '; p++)
886                                            ;
887                                    for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
888                                            ;
889                                    *q = '\0';
890                                    len = q - p;
891                                    if (*p != '\0')
892                                    {
893                                            memcpy(p_article_new->title, p, (size_t)len + 1);
894                                            memcpy(title_input, p_article_new->title, (size_t)len + 1);
895                                    }
896                                    ch = 0;
897                                    break;
898                            case 'C':
899                                    clearscr();
900                                    moveto(1, 1);
901                                    prints("取消...");
902                                    press_any_key();
903                                    goto cleanup;
904                            case '0':
905                            case '1':
906                            case '2':
907                            case '3':
908                                    sign_id = ch - '0';
909                                    break;
910                            default: // Invalid selection
911                                    continue;
912                            }
913    
914                            break;
915                    }
916    
917                    if (ch != CR || p_article_new->title[0] == '\0')
918                    {
919                            continue;
920                    }
921    
922                    for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
923                    {
924                            editor_display(p_editor_data);
925    
926                            clearscr();
927                            moveto(1, 1);
928                            prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
929                            iflush();
930    
931                            for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
932                            {
933                                    switch (toupper(ch))
934                                    {
935                                    case CR:
936                                            igetch_reset();
937                                    case 'S':
938                                            break;
939                                    case 'C':
940                                            clearscr();
941                                            moveto(1, 1);
942                                            prints("取消...");
943                                            press_any_key();
944                                            goto cleanup;
945                                    case 'T':
946                                            break;
947                                    case 'E':
948                                            break;
949                                    default: // Invalid selection
950                                            continue;
951                                    }
952    
953                                    break;
954                            }
955                    }
956    
957                    if (toupper(ch) != 'T')
958                    {
959                            break;
960                    }
961            }
962    
963            content = malloc(ARTICLE_CONTENT_MAX_LEN);
964            if (content == NULL)
965            {
966                    log_error("malloc(content) error: OOM\n");
967                    ret = -1;
968                    goto cleanup;
969            }
970    
971            len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN);
972            if (len_content < 0)
973            {
974                    log_error("editor_data_save() error\n");
975                    ret = -2;
976                    goto cleanup;
977            }
978    
979            db = db_open();
980            if (db == NULL)
981            {
982                    log_error("db_open() error: %s\n", mysql_error(db));
983                    ret = -1;
984                    goto cleanup;
985            }
986    
987            if (sign_id > 0)
988            {
989                    snprintf(sql, sizeof(sql),
990                                     "SELECT sign_%d AS sign FROM user_pubinfo WHERE UID = %d",
991                                     sign_id, BBS_priv.uid);
992    
993                    if (mysql_query(db, sql) != 0)
994                    {
995                            log_error("Query sign error: %s\n", mysql_error(db));
996                            ret = -1;
997                            goto cleanup;
998                    }
999                    if ((rs = mysql_use_result(db)) == NULL)
1000                    {
1001                            log_error("Get sign data failed\n");
1002                            ret = -1;
1003                            goto cleanup;
1004                    }
1005    
1006                    if ((row = mysql_fetch_row(rs)))
1007                    {
1008                            len_content += snprintf(content + len_content,
1009                                                                            ARTICLE_CONTENT_MAX_LEN - (size_t)len_content,
1010                                                                            "\n\n--\n%s\n", row[0]);
1011                    }
1012                    mysql_free_result(rs);
1013                    rs = NULL;
1014            }
1015    
1016            // Begin transaction
1017            if (mysql_query(db, "SET autocommit=0") != 0)
1018            {
1019                    log_error("SET autocommit=0 error: %s\n", mysql_error(db));
1020                    ret = -1;
1021                    goto cleanup;
1022            }
1023    
1024            if (mysql_query(db, "BEGIN") != 0)
1025            {
1026                    log_error("Begin transaction error: %s\n", mysql_error(db));
1027                    ret = -1;
1028                    goto cleanup;
1029            }
1030    
1031            // Secure SQL parameters
1032            content_f = malloc((size_t)len_content * 2 + 1);
1033            if (content_f == NULL)
1034            {
1035                    log_error("malloc(content_f) error: OOM\n");
1036                    ret = -1;
1037                    goto cleanup;
1038            }
1039    
1040            mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
1041            mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
1042            mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
1043    
1044            free(content);
1045            content = NULL;
1046    
1047            // Add content
1048            sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
1049            if (sql_content == NULL)
1050            {
1051                    log_error("malloc(sql_content) error: OOM\n");
1052                    ret = -1;
1053                    goto cleanup;
1054            }
1055    
1056            snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
1057                             "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1058                             content_f);
1059    
1060            free(content_f);
1061            content_f = NULL;
1062    
1063            if (mysql_query(db, sql_content) != 0)
1064            {
1065                    log_error("Add article content error: %s\n", mysql_error(db));
1066                    ret = -1;
1067                    goto cleanup;
1068            }
1069    
1070            p_article_new->cid = (int32_t)mysql_insert_id(db);
1071    
1072            free(sql_content);
1073            sql_content = NULL;
1074    
1075            // Add article
1076            snprintf(sql, sizeof(sql),
1077                             "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
1078                             "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
1079                             "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %ld)",
1080                             p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1081                             BBS_priv.uid, BBS_username, nickname_f, title_f,
1082                             p_article_new->cid, hostaddr_client, BBS_user_exp, len_content);
1083    
1084            if (mysql_query(db, sql) != 0)
1085            {
1086                    log_error("Add article error: %s\n", mysql_error(db));
1087                    ret = -1;
1088                    goto cleanup;
1089            }
1090    
1091            p_article_new->aid = (int32_t)mysql_insert_id(db);
1092    
1093            // Update topic article
1094            snprintf(sql, sizeof(sql),
1095                             "UPDATE bbs SET reply_count = reply_count + 1, "
1096                             "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "
1097                             "last_reply_nickname = '%s' WHERE AID = %d",
1098                             BBS_priv.uid, BBS_username, nickname_f,
1099                             (p_article->tid == 0 ? p_article->aid : p_article->tid));
1100    
1101            if (mysql_query(db, sql) != 0)
1102            {
1103                    log_error("Update topic article error: %s\n", mysql_error(db));
1104                    ret = -1;
1105                    goto cleanup;
1106            }
1107    
1108            // Link content to article
1109            snprintf(sql, sizeof(sql),
1110                             "UPDATE bbs_content SET AID = %d WHERE CID = %d",
1111                             p_article_new->aid, p_article_new->cid);
1112    
1113            if (mysql_query(db, sql) != 0)
1114            {
1115                    log_error("Update content error: %s\n", mysql_error(db));
1116                    ret = -1;
1117                    goto cleanup;
1118            }
1119    
1120            // Add exp
1121            if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
1122            {
1123                    snprintf(sql, sizeof(sql),
1124                                     "UPDATE user_pubinfo SET exp = exp + %d WHERE UID = %d",
1125                                     3, BBS_priv.uid);
1126    
1127                    if (mysql_query(db, sql) != 0)
1128                    {
1129                            log_error("Update exp error: %s\n", mysql_error(db));
1130                            ret = -1;
1131                            goto cleanup;
1132                    }
1133            }
1134    
1135            // Add log
1136            snprintf(sql, sizeof(sql),
1137                             "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
1138                             "VALUES(%d, %d, 'A', NOW(), '%s')",
1139                             p_article_new->aid, BBS_priv.uid, hostaddr_client);
1140    
1141            if (mysql_query(db, sql) != 0)
1142            {
1143                    log_error("Add log error: %s\n", mysql_error(db));
1144                    ret = -1;
1145                    goto cleanup;
1146            }
1147    
1148            // Commit transaction
1149            if (mysql_query(db, "COMMIT") != 0)
1150            {
1151                    log_error("Commit transaction error: %s\n", mysql_error(db));
1152                    ret = -1;
1153                    goto cleanup;
1154            }
1155    
1156            clearscr();
1157            moveto(1, 1);
1158            prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1159            press_any_key();
1160            ret = 1; // Success
1161    
1162    cleanup:
1163            mysql_free_result(rs);
1164            mysql_close(db);
1165    
1166            // Cleanup buffers
1167            editor_data_cleanup(p_editor_data);
1168    
1169            free(sql_content);
1170            free(content);
1171            free(content_f);
1172    
1173          return 0;          return (int)ret;
1174  }  }


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

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