/[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.4 by sysadm, Fri Jun 13 12:17:21 2025 UTC Revision 1.10 by sysadm, Sat Jun 14 11:58:00 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            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("");          p_editor_data = editor_data_load("");
71          if (p_editor_data == NULL)          if (p_editor_data == NULL)
72          {          {
# Line 52  int article_post(SECTION_LIST *p_section Line 79  int article_post(SECTION_LIST *p_section
79          {          {
80                  clearscr();                  clearscr();
81                  moveto(21, 1);                  moveto(21, 1);
82                  prints("发表文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);                  prints("发表文章于 %s[%s] 讨论区,类型: %s", p_section->stitle, p_section->sname, (p_article_new->transship ? "转载" : "原创"));
83                  moveto(22, 1);                  moveto(22, 1);
84                  prints("文章标题: %s", (title[0] == '\0' ? "[无]" : title));                  prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
85                  moveto(23, 1);                  moveto(23, 1);
86                  prints("使用第 %d 个签名", sign_id);                  prints("使用第 %d 个签名", sign_id);
87    
# Line 63  int article_post(SECTION_LIST *p_section Line 90  int article_post(SECTION_LIST *p_section
90                          prints("    按0~3选签名档(0表示不使用)");                          prints("    按0~3选签名档(0表示不使用)");
91    
92                          moveto(24, 1);                          moveto(24, 1);
93                          prints("T改标题, C取消, Enter继续: ");                          prints("T改标题, C取消, Z设为转载, Y设为原创, Enter继续: ");
94                          iflush();                          iflush();
95                          ch = 0;                          ch = 0;
96                  }                  }
# Line 78  int article_post(SECTION_LIST *p_section Line 105  int article_post(SECTION_LIST *p_section
105                          case 'T':                          case 'T':
106                                  moveto(24, 1);                                  moveto(24, 1);
107                                  clrtoeol();                                  clrtoeol();
108                                  len = get_data(24, 1, "标题:", title_input, STR_INPUT_LEN, 1);                                  len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);
109                                  for (p = title_input; *p == ' '; p++)                                  for (p = title_input; *p == ' '; p++)
110                                          ;                                          ;
111                                  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 114  int article_post(SECTION_LIST *p_section
114                                  len = q - p;                                  len = q - p;
115                                  if (*p != '\0')                                  if (*p != '\0')
116                                  {                                  {
117                                          memcpy(title, p, (size_t)len + 1);                                          memcpy(p_article_new->title, p, (size_t)len + 1);
118                                          memcpy(title_input, title, (size_t)len + 1);                                          memcpy(title_input, p_article_new->title, (size_t)len + 1);
119                                  }                                  }
120                                  ch = 0;                                  ch = 0;
121                                  break;                                  break;
# Line 98  int article_post(SECTION_LIST *p_section Line 125  int article_post(SECTION_LIST *p_section
125                                  prints("取消...");                                  prints("取消...");
126                                  press_any_key();                                  press_any_key();
127                                  goto cleanup;                                  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':                          case '0':
135                          case '1':                          case '1':
136                          case '2':                          case '2':
# Line 111  int article_post(SECTION_LIST *p_section Line 144  int article_post(SECTION_LIST *p_section
144                          break;                          break;
145                  }                  }
146    
147                  if (ch != CR || title[0] == '\0')                  if (ch != CR || p_article_new->title[0] == '\0')
148                  {                  {
149                          continue;                          continue;
150                  }                  }
# Line 150  int article_post(SECTION_LIST *p_section Line 183  int article_post(SECTION_LIST *p_section
183                                  break;                                  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          // editor_data_save(p_editor_data, p_data_new, data_new_len);          // Add log
341          log_common("Debug: post article\n");          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:  cleanup:
368            mysql_close(db);
369    
370            // Cleanup buffers
371          editor_data_cleanup(p_editor_data);          editor_data_cleanup(p_editor_data);
372    
373          return 0;          free(sql_content);
374            free(content);
375            free(content_f);
376    
377            return (int)ret;
378  }  }
379    
380  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)
381  {  {
382          ARTICLE_CACHE cache;          MYSQL *db = NULL;
383          EDITOR_DATA *p_editor_data;          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)          if (p_section == NULL || p_article == NULL)
399          {          {
400                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
401          }          }
402    
403          if (article_cache_load(&cache, VAR_ARTICLE_CACHE_DIR, p_article) < 0)          if (p_article->excerption) // Modify is not allowed
404          {          {
405                  log_error("article_cache_load(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  clearscr();
406                  return -2;                  moveto(1, 1);
407                    prints("该文章无法被编辑,请联系版主。");
408                    press_any_key();
409    
410                    return 0;
411          }          }
412          p_editor_data = editor_data_load(cache.p_data);  
413          if (p_editor_data == NULL)          db = db_open();
414            if (db == NULL)
415          {          {
416                  log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  log_error("db_open() error: %s\n", mysql_error(db));
417                  return -2;                  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 (article_cache_unload(&cache) < 0)          if ((rs = mysql_use_result(db)) == NULL)
433          {          {
434                  log_error("article_cache_unload(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  log_error("Get article content data failed\n");
435                  return -2;                  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          editor_display(p_editor_data);          mysql_close(db);
453            db = NULL;
454    
455          // editor_data_save(p_editor_data, p_data_new, data_new_len);          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            // 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);          editor_data_cleanup(p_editor_data);
620    
621          return 0;          free(sql_content);
622            free(content);
623            free(content_f);
624    
625            return (int)ret;
626  }  }
627    
628  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)
629  {  {
630          ARTICLE_CACHE cache;          MYSQL *db = NULL;
631          EDITOR_DATA *p_editor_data;          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)          if (p_section == NULL || p_article == NULL)
654          {          {
655                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
656          }          }
657    
658          if (article_cache_load(&cache, VAR_ARTICLE_CACHE_DIR, p_article) < 0)          if (p_article->lock) // Reply is not allowed
659          {          {
660                  log_error("article_cache_load(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  clearscr();
661                  return -2;                  moveto(1, 1);
662                    prints("该文章谢绝回复");
663                    press_any_key();
664    
665                    return 0;
666          }          }
667          p_editor_data = editor_data_load(cache.p_data);  
668          if (p_editor_data == NULL)          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("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  log_error("Query article content error: %s\n", mysql_error(db));
689                  return -2;                  return -2;
690          }          }
691          if (article_cache_unload(&cache) < 0)          if ((rs = mysql_use_result(db)) == NULL)
692          {          {
693                  log_error("article_cache_unload(aid=%d, cid=%d) error\n", p_article->aid, p_article->cid);                  log_error("Get article content data failed\n");
694                  return -2;                  return -2;
695          }          }
696    
697          editor_display(p_editor_data);          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          // editor_data_save(p_editor_data, p_data_new, data_new_len);                  // 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                    if (content[len - 1] != '\n') // Appennd \n if not exist
735                    {
736                            content[len] = '\n';
737                            len++;
738                    }
739                    content[len] = '\0';
740    
741                    free(content_f);
742                    content_f = NULL;
743    
744                    p_editor_data = editor_data_load(content);
745                    if (p_editor_data == NULL)
746                    {
747                            log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
748                            ret = -3;
749                            goto cleanup;
750                    }
751    
752                    free(content);
753                    content = NULL;
754            }
755            mysql_free_result(rs);
756            rs = NULL;
757    
758            mysql_close(db);
759            db = NULL;
760    
761            // Set title and sign
762            for (ch = 'T'; !SYS_server_exit;)
763            {
764                    clearscr();
765                    moveto(21, 1);
766                    prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);
767                    moveto(22, 1);
768                    prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
769                    moveto(23, 1);
770                    prints("使用第 %d 个签名", sign_id);
771    
772                    if (toupper(ch) != 'T')
773                    {
774                            prints("    按0~3选签名档(0表示不使用)");
775    
776                            moveto(24, 1);
777                            prints("T改标题, C取消, Enter继续: ");
778                            iflush();
779                            ch = 0;
780                    }
781    
782                    for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
783                    {
784                            switch (toupper(ch))
785                            {
786                            case CR:
787                                    igetch_reset();
788                                    break;
789                            case 'T':
790                                    moveto(24, 1);
791                                    clrtoeol();
792                                    len = get_data(24, 1, "标题: ", title_input, TITLE_INPUT_MAX_LEN, 1);
793                                    for (p = title_input; *p == ' '; p++)
794                                            ;
795                                    for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
796                                            ;
797                                    *q = '\0';
798                                    len = q - p;
799                                    if (*p != '\0')
800                                    {
801                                            memcpy(p_article_new->title, p, (size_t)len + 1);
802                                            memcpy(title_input, p_article_new->title, (size_t)len + 1);
803                                    }
804                                    ch = 0;
805                                    break;
806                            case 'C':
807                                    clearscr();
808                                    moveto(1, 1);
809                                    prints("取消...");
810                                    press_any_key();
811                                    goto cleanup;
812                            case '0':
813                            case '1':
814                            case '2':
815                            case '3':
816                                    sign_id = ch - '0';
817                                    break;
818                            default: // Invalid selection
819                                    continue;
820                            }
821    
822                            break;
823                    }
824    
825                    if (ch != CR || p_article_new->title[0] == '\0')
826                    {
827                            continue;
828                    }
829    
830                    for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
831                    {
832                            editor_display(p_editor_data);
833    
834                            clearscr();
835                            moveto(1, 1);
836                            prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
837                            iflush();
838    
839                            for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
840                            {
841                                    switch (toupper(ch))
842                                    {
843                                    case CR:
844                                            igetch_reset();
845                                    case 'S':
846                                            break;
847                                    case 'C':
848                                            clearscr();
849                                            moveto(1, 1);
850                                            prints("取消...");
851                                            press_any_key();
852                                            goto cleanup;
853                                    case 'T':
854                                            break;
855                                    case 'E':
856                                            break;
857                                    default: // Invalid selection
858                                            continue;
859                                    }
860    
861                                    break;
862                            }
863                    }
864    
865                    if (toupper(ch) != 'T')
866                    {
867                            break;
868                    }
869            }
870    
871            content = malloc(ARTICLE_CONTENT_MAX_LEN);
872            if (content == NULL)
873            {
874                    log_error("malloc(content) error: OOM\n");
875                    ret = -1;
876                    goto cleanup;
877            }
878    
879            len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN);
880            if (len_content < 0)
881            {
882                    log_error("editor_data_save() error\n");
883                    ret = -2;
884                    goto cleanup;
885            }
886    
887            db = db_open();
888            if (db == NULL)
889            {
890                    log_error("db_open() error: %s\n", mysql_error(db));
891                    ret = -1;
892                    goto cleanup;
893            }
894    
895            if (sign_id > 0)
896            {
897                    snprintf(sql, sizeof(sql),
898                                     "SELECT sign_%d AS sign FROM user_pubinfo WHERE UID = %d",
899                                     sign_id, BBS_priv.uid);
900    
901                    if (mysql_query(db, sql) != 0)
902                    {
903                            log_error("Query sign error: %s\n", mysql_error(db));
904                            ret = -1;
905                            goto cleanup;
906                    }
907                    if ((rs = mysql_use_result(db)) == NULL)
908                    {
909                            log_error("Get sign data failed\n");
910                            ret = -1;
911                            goto cleanup;
912                    }
913    
914                    if ((row = mysql_fetch_row(rs)))
915                    {
916                            len_content += snprintf(content + len_content,
917                                                                            ARTICLE_CONTENT_MAX_LEN - (size_t)len_content,
918                                                                            "\n\n--\n%s\n", row[0]);
919                    }
920                    mysql_free_result(rs);
921                    rs = NULL;
922            }
923    
924            content_f = malloc((size_t)len_content * 2 + 1);
925            if (content_f == NULL)
926            {
927                    log_error("malloc(content_f) error: OOM\n");
928                    ret = -1;
929                    goto cleanup;
930            }
931    
932            sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
933            if (sql_content == NULL)
934            {
935                    log_error("malloc(sql_content) error: OOM\n");
936                    ret = -1;
937                    goto cleanup;
938            }
939    
940            // Begin transaction
941            if (mysql_query(db, "SET autocommit=0") != 0)
942            {
943                    log_error("SET autocommit=0 error: %s\n", mysql_error(db));
944                    ret = -1;
945                    goto cleanup;
946            }
947    
948            if (mysql_query(db, "BEGIN") != 0)
949            {
950                    log_error("Begin transaction error: %s\n", mysql_error(db));
951                    ret = -1;
952                    goto cleanup;
953            }
954    
955            // Secure SQL parameters
956            mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
957            mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
958            mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
959    
960            // Add content
961            snprintf(sql, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
962                             "INSERT INTO bbs_content(AID, content) values(0, '%s')",
963                             content_f);
964    
965            if (mysql_query(db, sql) != 0)
966            {
967                    log_error("Add article content error: %s\n", mysql_error(db));
968                    ret = -1;
969                    goto cleanup;
970            }
971    
972            p_article_new->cid = (int32_t)mysql_insert_id(db);
973    
974            // Add article
975            snprintf(sql, sizeof(sql),
976                             "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
977                             "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
978                             "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %ld)",
979                             p_section->sid, p_article->aid, BBS_priv.uid, BBS_username, nickname_f, title_f,
980                             p_article_new->cid, hostaddr_client, BBS_user_exp, len_content);
981    
982            if (mysql_query(db, sql) != 0)
983            {
984                    log_error("Add article error: %s\n", mysql_error(db));
985                    ret = -1;
986                    goto cleanup;
987            }
988    
989            p_article_new->aid = (int32_t)mysql_insert_id(db);
990    
991            // Update topic article
992            snprintf(sql, sizeof(sql),
993                             "UPDATE bbs SET reply_count = reply_count + 1, "
994                             "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "
995                             "last_reply_nickname = '%s' WHERE Aid = %d",
996                             BBS_priv.uid, BBS_username, nickname_f, p_article->aid);
997    
998            // Link content to article
999            snprintf(sql, sizeof(sql),
1000                             "UPDATE bbs_content SET AID = %d WHERE CID = %d",
1001                             p_article_new->aid, p_article_new->cid);
1002    
1003            if (mysql_query(db, sql) != 0)
1004            {
1005                    log_error("Update content error: %s\n", mysql_error(db));
1006                    ret = -1;
1007                    goto cleanup;
1008            }
1009    
1010            // Add exp
1011            if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
1012            {
1013                    snprintf(sql, sizeof(sql),
1014                                     "UPDATE user_pubinfo SET exp = exp + %d WHERE UID = %d",
1015                                     3, BBS_priv.uid);
1016    
1017                    if (mysql_query(db, sql) != 0)
1018                    {
1019                            log_error("Update exp error: %s\n", mysql_error(db));
1020                            ret = -1;
1021                            goto cleanup;
1022                    }
1023            }
1024    
1025            // Add log
1026            snprintf(sql, sizeof(sql),
1027                             "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
1028                             "VALUES(%d, %d, 'A', NOW(), '%s')",
1029                             p_article_new->aid, BBS_priv.uid, hostaddr_client);
1030    
1031            if (mysql_query(db, sql) != 0)
1032            {
1033                    log_error("Add log error: %s\n", mysql_error(db));
1034                    ret = -1;
1035                    goto cleanup;
1036            }
1037    
1038            // Commit transaction
1039            if (mysql_query(db, "COMMIT") != 0)
1040            {
1041                    log_error("Commit transaction error: %s\n", mysql_error(db));
1042                    ret = -1;
1043                    goto cleanup;
1044            }
1045    
1046            clearscr();
1047            moveto(1, 1);
1048            prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1049            press_any_key();
1050            ret = 1; // Success
1051    
1052    cleanup:
1053            mysql_free_result(rs);
1054            mysql_close(db);
1055    
1056            // Cleanup buffers
1057          editor_data_cleanup(p_editor_data);          editor_data_cleanup(p_editor_data);
1058    
1059          return 0;          free(sql_content);
1060            free(content);
1061            free(content_f);
1062    
1063            return (int)ret;
1064  }  }


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

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