/[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.38 - (hide annotations)
Tue Nov 4 14:58:56 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.37: +1 -1 lines
Content type: text/x-csrc
Refine file header information comments

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

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