/[LeafOK_CVS]/lbbs/src/article_post.c
ViewVC logotype

Annotation of /lbbs/src/article_post.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.40 - (hide annotations)
Wed Nov 5 03:17:12 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.39: +6 -5 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers

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

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