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


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

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