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


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

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