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


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

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