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


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

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