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


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

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