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


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

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