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


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

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