/[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.30 - (hide annotations)
Fri Oct 10 04:33:40 2025 UTC (5 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.29: +1 -1 lines
Content type: text/x-csrc
Set reply_note = 0 as default in article_reply()

1 sysadm 1.1 /***************************************************************************
2     article_post.c - description
3     -------------------
4     copyright : (C) 2004-2025 by Leaflet
5     email : leaflet@leafok.com
6     ***************************************************************************/
7    
8     /***************************************************************************
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 3 of the License, or *
13     * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17 sysadm 1.22 #include "article_cache.h"
18 sysadm 1.1 #include "article_post.h"
19 sysadm 1.22 #include "bbs.h"
20     #include "database.h"
21 sysadm 1.1 #include "editor.h"
22 sysadm 1.22 #include "io.h"
23 sysadm 1.1 #include "log.h"
24 sysadm 1.6 #include "lml.h"
25 sysadm 1.22 #include "screen.h"
26 sysadm 1.8 #include "user_priv.h"
27 sysadm 1.3 #include <ctype.h>
28     #include <string.h>
29 sysadm 1.6 #include <stdlib.h>
30 sysadm 1.8 #include <time.h>
31 sysadm 1.1
32 sysadm 1.24 #define TITLE_INPUT_MAX_LEN 72
33 sysadm 1.6 #define ARTICLE_CONTENT_MAX_LEN 1024 * 1024 * 4 // 4MB
34     #define ARTICLE_QUOTE_MAX_LINES 20
35     #define ARTICLE_QUOTE_LINE_MAX_LEN 76
36 sysadm 1.3
37 sysadm 1.8 #define MODIFY_DT_MAX_LEN 50
38    
39     int article_post(const SECTION_LIST *p_section, ARTICLE *p_article_new)
40 sysadm 1.1 {
41 sysadm 1.8 MYSQL *db = NULL;
42     MYSQL_RES *rs = NULL;
43     MYSQL_ROW row;
44     char sql[SQL_BUFFER_LEN];
45     char *sql_content = NULL;
46 sysadm 1.6 EDITOR_DATA *p_editor_data = NULL;
47 sysadm 1.24 char title_input[BBS_article_title_max_len + 1];
48 sysadm 1.8 char title_f[BBS_article_title_max_len * 2 + 1];
49     char *content = NULL;
50     char *content_f = NULL;
51     long len_content;
52 sysadm 1.24 int content_display_length;
53 sysadm 1.8 char nickname_f[BBS_nickname_max_len * 2 + 1];
54 sysadm 1.3 int sign_id = 0;
55 sysadm 1.29 int reply_note = 1;
56 sysadm 1.3 long len;
57     int ch;
58     char *p, *q;
59 sysadm 1.8 long ret = 0;
60 sysadm 1.3
61 sysadm 1.8 if (p_section == NULL || p_article_new == NULL)
62 sysadm 1.3 {
63     log_error("NULL pointer error\n");
64     }
65 sysadm 1.2
66 sysadm 1.12 if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
67     {
68     clearscr();
69     moveto(1, 1);
70 sysadm 1.24 prints("您没有权限在本版块发表文章\n");
71 sysadm 1.12 press_any_key();
72    
73     return 0;
74     }
75    
76 sysadm 1.8 p_article_new->title[0] = '\0';
77 sysadm 1.6 title_input[0] = '\0';
78 sysadm 1.8 p_article_new->transship = 0;
79 sysadm 1.6
80 sysadm 1.3 p_editor_data = editor_data_load("");
81     if (p_editor_data == NULL)
82     {
83     log_error("editor_data_load() error\n");
84 sysadm 1.16 ret = -1;
85     goto cleanup;
86 sysadm 1.3 }
87 sysadm 1.1
88 sysadm 1.3 // Set title and sign
89     for (ch = 'T'; !SYS_server_exit;)
90 sysadm 1.2 {
91 sysadm 1.3 clearscr();
92     moveto(21, 1);
93 sysadm 1.29 prints("发表文章于 %s[%s] 讨论区,类型: %s,通知:%s",
94     p_section->stitle, p_section->sname,
95     (p_article_new->transship ? "转载" : "原创"),
96     (reply_note ? "开启" : "关闭"));
97 sysadm 1.3 moveto(22, 1);
98 sysadm 1.24 prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
99 sysadm 1.3 moveto(23, 1);
100 sysadm 1.24 prints("使用第 %d 个签名", sign_id);
101 sysadm 1.3
102     if (toupper(ch) != 'T')
103 sysadm 1.2 {
104 sysadm 1.24 prints(" 按0~3选签名档(0表示不使用)");
105 sysadm 1.3
106     moveto(24, 1);
107 sysadm 1.29 prints("T改标题, C取消, Z设为%s, N%s, Enter继续: ",
108     (p_article_new->transship ? "原创" : "转载"),
109     (reply_note ? "通知关闭" : "通知开启"));
110 sysadm 1.3 iflush();
111     ch = 0;
112 sysadm 1.2 }
113 sysadm 1.3
114     for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
115 sysadm 1.2 {
116 sysadm 1.3 switch (toupper(ch))
117     {
118 sysadm 1.19 case KEY_NULL:
119     case KEY_TIMEOUT:
120     goto cleanup;
121 sysadm 1.3 case CR:
122     igetch_reset();
123     break;
124     case 'T':
125 sysadm 1.27 len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
126 sysadm 1.3 for (p = title_input; *p == ' '; p++)
127     ;
128     for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
129     ;
130     *q = '\0';
131     len = q - p;
132     if (*p != '\0')
133     {
134 sysadm 1.8 memcpy(p_article_new->title, p, (size_t)len + 1);
135     memcpy(title_input, p_article_new->title, (size_t)len + 1);
136 sysadm 1.3 }
137 sysadm 1.4 ch = 0;
138 sysadm 1.3 break;
139     case 'C':
140     clearscr();
141     moveto(1, 1);
142 sysadm 1.24 prints("取消...");
143 sysadm 1.3 press_any_key();
144     goto cleanup;
145 sysadm 1.29 case 'Z':
146     p_article_new->transship = (p_article_new->transship ? 0 : 1);
147 sysadm 1.8 break;
148 sysadm 1.29 case 'N':
149     reply_note = (reply_note ? 0 : 1);
150 sysadm 1.8 break;
151 sysadm 1.3 case '0':
152     case '1':
153     case '2':
154     case '3':
155     sign_id = ch - '0';
156     break;
157     default: // Invalid selection
158     continue;
159     }
160    
161     break;
162 sysadm 1.2 }
163 sysadm 1.3
164 sysadm 1.8 if (ch != CR || p_article_new->title[0] == '\0')
165 sysadm 1.2 {
166 sysadm 1.3 continue;
167 sysadm 1.2 }
168 sysadm 1.3
169     for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
170 sysadm 1.2 {
171 sysadm 1.3 editor_display(p_editor_data);
172    
173     clearscr();
174     moveto(1, 1);
175 sysadm 1.24 prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
176 sysadm 1.3 iflush();
177    
178     for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
179     {
180     switch (toupper(ch))
181     {
182 sysadm 1.19 case KEY_NULL:
183     case KEY_TIMEOUT:
184     goto cleanup;
185 sysadm 1.3 case CR:
186     igetch_reset();
187     case 'S':
188     break;
189     case 'C':
190     clearscr();
191     moveto(1, 1);
192 sysadm 1.24 prints("取消...");
193 sysadm 1.3 press_any_key();
194     goto cleanup;
195     case 'T':
196     break;
197     case 'E':
198     break;
199     default: // Invalid selection
200     continue;
201     }
202    
203     break;
204     }
205 sysadm 1.2 }
206 sysadm 1.5
207     if (toupper(ch) != 'T')
208     {
209     break;
210     }
211 sysadm 1.3 }
212    
213 sysadm 1.19 if (SYS_server_exit) // Do not save data on shutdown
214     {
215     goto cleanup;
216     }
217    
218 sysadm 1.8 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 sysadm 1.16 ret = -1;
231 sysadm 1.8 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 sysadm 1.24 // Calculate display length of content
272     content_display_length = str_length(content, 1);
273    
274 sysadm 1.8 // 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 sysadm 1.13 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 sysadm 1.8 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 sysadm 1.13 free(content);
303     content = NULL;
304    
305 sysadm 1.8 // Add content
306 sysadm 1.13 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 sysadm 1.8 "INSERT INTO bbs_content(AID, content) values(0, '%s')",
316     content_f);
317    
318 sysadm 1.13 free(content_f);
319     content_f = NULL;
320    
321     if (mysql_query(db, sql_content) != 0)
322 sysadm 1.8 {
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 sysadm 1.13 free(sql_content);
331     sql_content = NULL;
332    
333 sysadm 1.8 // 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 sysadm 1.29 "VALUES(%d, 0, %d, '%s', '%s', '%s', %d, %d, NOW(), '%s', %d, %d, NOW(), 1, %d)",
338 sysadm 1.8 p_section->sid, BBS_priv.uid, BBS_username, nickname_f, title_f,
339 sysadm 1.29 p_article_new->cid, p_article_new->transship, hostaddr_client,
340     reply_note, BBS_user_exp, content_display_length);
341 sysadm 1.8
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 sysadm 1.17 mysql_close(db);
400     db = NULL;
401    
402 sysadm 1.8 clearscr();
403     moveto(1, 1);
404 sysadm 1.24 prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
405 sysadm 1.8 press_any_key();
406     ret = 1; // Success
407 sysadm 1.3
408     cleanup:
409 sysadm 1.8 mysql_close(db);
410    
411     // Cleanup buffers
412 sysadm 1.3 editor_data_cleanup(p_editor_data);
413    
414 sysadm 1.8 free(sql_content);
415     free(content);
416     free(content_f);
417    
418     return (int)ret;
419 sysadm 1.3 }
420    
421 sysadm 1.8 int article_modify(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
422 sysadm 1.3 {
423 sysadm 1.8 MYSQL *db = NULL;
424     MYSQL_RES *rs = NULL;
425 sysadm 1.6 MYSQL_ROW row;
426     char sql[SQL_BUFFER_LEN];
427 sysadm 1.8 char *sql_content = NULL;
428     char *content = NULL;
429     char *content_f = NULL;
430     long len_content;
431 sysadm 1.24 int content_display_length;
432 sysadm 1.29 int reply_note = 1;
433 sysadm 1.6 int ch;
434 sysadm 1.8 long ret = 0;
435     time_t now;
436     struct tm tm_modify_dt;
437     char str_modify_dt[MODIFY_DT_MAX_LEN + 1];
438 sysadm 1.6
439     EDITOR_DATA *p_editor_data = NULL;
440 sysadm 1.3
441     if (p_section == NULL || p_article == NULL)
442     {
443     log_error("NULL pointer error\n");
444     }
445    
446 sysadm 1.8 if (p_article->excerption) // Modify is not allowed
447     {
448     clearscr();
449     moveto(1, 1);
450 sysadm 1.24 prints("该文章无法被编辑,请联系版主。");
451 sysadm 1.8 press_any_key();
452    
453     return 0;
454     }
455    
456 sysadm 1.6 db = db_open();
457     if (db == NULL)
458 sysadm 1.3 {
459 sysadm 1.6 log_error("db_open() error: %s\n", mysql_error(db));
460 sysadm 1.16 ret = -1;
461     goto cleanup;
462 sysadm 1.6 }
463    
464     snprintf(sql, sizeof(sql),
465 sysadm 1.29 "SELECT bbs_content.CID, bbs_content.content, reply_note "
466 sysadm 1.6 "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "
467     "WHERE bbs.AID = %d",
468     p_article->aid);
469    
470     if (mysql_query(db, sql) != 0)
471     {
472     log_error("Query article content error: %s\n", mysql_error(db));
473 sysadm 1.16 ret = -1;
474 sysadm 1.8 goto cleanup;
475 sysadm 1.3 }
476 sysadm 1.6 if ((rs = mysql_use_result(db)) == NULL)
477 sysadm 1.3 {
478 sysadm 1.6 log_error("Get article content data failed\n");
479 sysadm 1.16 ret = -1;
480 sysadm 1.8 goto cleanup;
481 sysadm 1.3 }
482 sysadm 1.5
483 sysadm 1.6 if ((row = mysql_fetch_row(rs)))
484 sysadm 1.3 {
485 sysadm 1.13 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 sysadm 1.20 len_content = str_filter(content, 0);
498 sysadm 1.13
499     p_editor_data = editor_data_load(content);
500 sysadm 1.6 if (p_editor_data == NULL)
501     {
502     log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
503 sysadm 1.16 ret = -1;
504 sysadm 1.8 goto cleanup;
505 sysadm 1.6 }
506 sysadm 1.13
507     free(content);
508     content = NULL;
509 sysadm 1.29
510     reply_note = atoi(row[2]);
511 sysadm 1.3 }
512 sysadm 1.6 mysql_free_result(rs);
513 sysadm 1.8 rs = NULL;
514    
515 sysadm 1.6 mysql_close(db);
516 sysadm 1.8 db = NULL;
517 sysadm 1.3
518 sysadm 1.6 for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
519     {
520     editor_display(p_editor_data);
521    
522 sysadm 1.29 while (!SYS_server_exit)
523     {
524     clearscr();
525     moveto(1, 1);
526     prints("(S)保存, (C)取消, (N)通知%s or (E)再编辑? [S]: ",
527     (reply_note ? "关闭" : "开启"));
528     iflush();
529 sysadm 1.6
530 sysadm 1.29 ch = igetch_t(MAX_DELAY_TIME);
531 sysadm 1.6 switch (toupper(ch))
532     {
533 sysadm 1.19 case KEY_NULL:
534     case KEY_TIMEOUT:
535     goto cleanup;
536 sysadm 1.6 case CR:
537     igetch_reset();
538     case 'S':
539     break;
540     case 'C':
541     clearscr();
542     moveto(1, 1);
543 sysadm 1.24 prints("取消...");
544 sysadm 1.6 press_any_key();
545     goto cleanup;
546 sysadm 1.29 case 'N':
547     reply_note = (reply_note ? 0 : 1);
548     continue;
549 sysadm 1.6 case 'E':
550     break;
551     default: // Invalid selection
552     continue;
553     }
554    
555     break;
556     }
557     }
558 sysadm 1.3
559 sysadm 1.19 if (SYS_server_exit) // Do not save data on shutdown
560     {
561     goto cleanup;
562     }
563    
564 sysadm 1.8 // 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 sysadm 1.16 ret = -1;
578 sysadm 1.8 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 sysadm 1.24 "\n--\n※ 作者已于 %s 修改本文※\n",
587 sysadm 1.8 str_modify_dt);
588    
589 sysadm 1.24 // Calculate display length of content
590     content_display_length = str_length(content, 1);
591    
592 sysadm 1.8 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 sysadm 1.13 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 sysadm 1.8 mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
625    
626 sysadm 1.13 free(content);
627     content = NULL;
628    
629 sysadm 1.8 // Add content
630 sysadm 1.13 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 sysadm 1.8 "INSERT INTO bbs_content(AID, content) values(%d, '%s')",
640     p_article->aid, content_f);
641    
642 sysadm 1.13 free(content_f);
643     content_f = NULL;
644    
645     if (mysql_query(db, sql_content) != 0)
646 sysadm 1.8 {
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 sysadm 1.13 free(sql_content);
655     sql_content = NULL;
656    
657 sysadm 1.8 // Update article
658     snprintf(sql, sizeof(sql),
659 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
660     p_article_new->cid, content_display_length, reply_note, p_article->aid);
661 sysadm 1.8
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 sysadm 1.17 mysql_close(db);
698     db = NULL;
699    
700 sysadm 1.8 clearscr();
701     moveto(1, 1);
702 sysadm 1.24 prints("修改完成,新内容通常会在%d秒后可见", BBS_section_list_load_interval);
703 sysadm 1.8 press_any_key();
704     ret = 1; // Success
705 sysadm 1.3
706 sysadm 1.6 cleanup:
707 sysadm 1.13 mysql_free_result(rs);
708 sysadm 1.8 mysql_close(db);
709    
710     // Cleanup buffers
711 sysadm 1.3 editor_data_cleanup(p_editor_data);
712    
713 sysadm 1.8 free(sql_content);
714     free(content);
715     free(content_f);
716    
717     return (int)ret;
718 sysadm 1.3 }
719    
720 sysadm 1.8 int article_reply(const SECTION_LIST *p_section, const ARTICLE *p_article, ARTICLE *p_article_new)
721 sysadm 1.3 {
722 sysadm 1.8 MYSQL *db = NULL;
723     MYSQL_RES *rs = NULL;
724 sysadm 1.6 MYSQL_ROW row;
725 sysadm 1.8 long line_offsets[ARTICLE_QUOTE_MAX_LINES + 1];
726 sysadm 1.6 char sql[SQL_BUFFER_LEN];
727 sysadm 1.8 char *sql_content = NULL;
728 sysadm 1.6 EDITOR_DATA *p_editor_data = NULL;
729 sysadm 1.8 char title_input[BBS_article_title_max_len + sizeof("Re: ")];
730     char title_f[BBS_article_title_max_len * 2 + 1];
731     char *content = NULL;
732     char *content_f = NULL;
733     long len_content;
734 sysadm 1.24 int content_display_length;
735 sysadm 1.8 char nickname_f[BBS_nickname_max_len * 2 + 1];
736 sysadm 1.6 int sign_id = 0;
737 sysadm 1.30 int reply_note = 0;
738 sysadm 1.6 long len;
739     int ch;
740     char *p, *q;
741     int eol;
742     int display_len;
743     long quote_content_lines;
744     long i;
745 sysadm 1.8 long ret = 0;
746 sysadm 1.14 int topic_locked = 0;
747 sysadm 1.28 char msg[BBS_msg_max_len];
748     char msg_f[BBS_msg_max_len * 2 + 1];
749     int len_msg;
750 sysadm 1.3
751     if (p_section == NULL || p_article == NULL)
752     {
753     log_error("NULL pointer error\n");
754     }
755    
756 sysadm 1.12 if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
757     {
758     clearscr();
759     moveto(1, 1);
760 sysadm 1.24 prints("您没有权限在本版块发表文章\n");
761 sysadm 1.12 press_any_key();
762    
763     return 0;
764     }
765    
766 sysadm 1.8 p_article_new->title[0] = '\0';
767 sysadm 1.6 snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
768 sysadm 1.20 len = split_line(title_input, TITLE_INPUT_MAX_LEN, &eol, &display_len, 0);
769 sysadm 1.6 title_input[len] = '\0';
770    
771     db = db_open();
772     if (db == NULL)
773     {
774     log_error("db_open() error: %s\n", mysql_error(db));
775 sysadm 1.16 ret = -1;
776     goto cleanup;
777 sysadm 1.6 }
778    
779     snprintf(sql, sizeof(sql),
780 sysadm 1.14 "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 sysadm 1.16 ret = -1;
787     goto cleanup;
788 sysadm 1.14 }
789     if ((rs = mysql_store_result(db)) == NULL)
790     {
791     log_error("Get article status data failed\n");
792 sysadm 1.16 ret = -1;
793     goto cleanup;
794 sysadm 1.14 }
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 sysadm 1.17 mysql_close(db);
809     db = NULL;
810 sysadm 1.18
811 sysadm 1.14 clearscr();
812     moveto(1, 1);
813 sysadm 1.24 prints("该主题谢绝回复");
814 sysadm 1.14 press_any_key();
815    
816     goto cleanup;
817     }
818    
819     snprintf(sql, sizeof(sql),
820 sysadm 1.6 "SELECT bbs_content.CID, bbs_content.content "
821     "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "
822     "WHERE bbs.AID = %d",
823     p_article->aid);
824    
825     if (mysql_query(db, sql) != 0)
826 sysadm 1.3 {
827 sysadm 1.6 log_error("Query article content error: %s\n", mysql_error(db));
828 sysadm 1.16 ret = -1;
829     goto cleanup;
830 sysadm 1.6 }
831     if ((rs = mysql_use_result(db)) == NULL)
832     {
833     log_error("Get article content data failed\n");
834 sysadm 1.16 ret = -1;
835     goto cleanup;
836 sysadm 1.3 }
837 sysadm 1.5
838 sysadm 1.6 if ((row = mysql_fetch_row(rs)))
839     {
840 sysadm 1.8 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 sysadm 1.6 // Apply LML render to content body
857 sysadm 1.26 len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 0);
858 sysadm 1.6 content_f[len] = '\0';
859    
860 sysadm 1.7 // Remove control sequence
861 sysadm 1.20 len = str_filter(content_f, 0);
862 sysadm 1.7
863 sysadm 1.8 len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
864 sysadm 1.24 "\n\n【 在 %s (%s) 的大作中提到: 】\n",
865 sysadm 1.8 p_article->username, p_article->nickname);
866 sysadm 1.7
867 sysadm 1.23 quote_content_lines = split_data_lines(content_f, ARTICLE_QUOTE_LINE_MAX_LEN, line_offsets, ARTICLE_QUOTE_MAX_LINES + 1, 0, NULL);
868 sysadm 1.7 for (i = 0; i < quote_content_lines; i++)
869 sysadm 1.6 {
870     memcpy(content + len, ": ", 2); // quote line prefix
871     len += 2;
872     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]);
874 sysadm 1.18 if (content[len - 1] != '\n') // Appennd \n if not exist
875     {
876     content[len] = '\n';
877     len++;
878     }
879 sysadm 1.6 }
880 sysadm 1.9 if (content[len - 1] != '\n') // Appennd \n if not exist
881     {
882     content[len] = '\n';
883     len++;
884     }
885 sysadm 1.6 content[len] = '\0';
886 sysadm 1.8
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 sysadm 1.16 ret = -1;
895 sysadm 1.8 goto cleanup;
896     }
897    
898     free(content);
899     content = NULL;
900 sysadm 1.6 }
901     mysql_free_result(rs);
902 sysadm 1.8 rs = NULL;
903    
904 sysadm 1.6 mysql_close(db);
905 sysadm 1.8 db = NULL;
906 sysadm 1.5
907 sysadm 1.6 // Set title and sign
908     for (ch = 'T'; !SYS_server_exit;)
909 sysadm 1.3 {
910 sysadm 1.6 clearscr();
911     moveto(21, 1);
912 sysadm 1.29 prints("回复文章于 %s[%s] 讨论区,通知:%s", p_section->stitle, p_section->sname, (reply_note ? "开启" : "关闭"));
913 sysadm 1.6 moveto(22, 1);
914 sysadm 1.24 prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
915 sysadm 1.6 moveto(23, 1);
916 sysadm 1.24 prints("使用第 %d 个签名", sign_id);
917 sysadm 1.6
918     if (toupper(ch) != 'T')
919     {
920 sysadm 1.24 prints(" 按0~3选签名档(0表示不使用)");
921 sysadm 1.6
922     moveto(24, 1);
923 sysadm 1.29 prints("T改标题, C取消, N%s, Enter继续: ",
924     (reply_note ? "通知关闭" : "通知开启"));
925 sysadm 1.6 iflush();
926     ch = 0;
927     }
928    
929     for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
930     {
931     switch (toupper(ch))
932     {
933 sysadm 1.19 case KEY_NULL:
934     case KEY_TIMEOUT:
935     goto cleanup;
936 sysadm 1.6 case CR:
937     igetch_reset();
938     break;
939     case 'T':
940 sysadm 1.27 len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN);
941 sysadm 1.6 for (p = title_input; *p == ' '; p++)
942     ;
943     for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
944     ;
945     *q = '\0';
946     len = q - p;
947     if (*p != '\0')
948     {
949 sysadm 1.8 memcpy(p_article_new->title, p, (size_t)len + 1);
950     memcpy(title_input, p_article_new->title, (size_t)len + 1);
951 sysadm 1.6 }
952     ch = 0;
953     break;
954     case 'C':
955     clearscr();
956     moveto(1, 1);
957 sysadm 1.24 prints("取消...");
958 sysadm 1.6 press_any_key();
959     goto cleanup;
960 sysadm 1.29 case 'N':
961     reply_note = (reply_note ? 0 : 1);
962     break;
963 sysadm 1.6 case '0':
964     case '1':
965     case '2':
966     case '3':
967     sign_id = ch - '0';
968     break;
969     default: // Invalid selection
970     continue;
971     }
972    
973     break;
974     }
975    
976 sysadm 1.8 if (ch != CR || p_article_new->title[0] == '\0')
977 sysadm 1.6 {
978     continue;
979     }
980    
981     for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
982     {
983     editor_display(p_editor_data);
984    
985     clearscr();
986     moveto(1, 1);
987 sysadm 1.24 prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
988 sysadm 1.6 iflush();
989    
990     for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
991     {
992     switch (toupper(ch))
993     {
994 sysadm 1.19 case KEY_NULL:
995     case KEY_TIMEOUT:
996     goto cleanup;
997 sysadm 1.6 case CR:
998     igetch_reset();
999     case 'S':
1000     break;
1001     case 'C':
1002     clearscr();
1003     moveto(1, 1);
1004 sysadm 1.24 prints("取消...");
1005 sysadm 1.6 press_any_key();
1006     goto cleanup;
1007     case 'T':
1008     break;
1009     case 'E':
1010     break;
1011     default: // Invalid selection
1012     continue;
1013     }
1014    
1015     break;
1016     }
1017     }
1018    
1019     if (toupper(ch) != 'T')
1020     {
1021     break;
1022     }
1023 sysadm 1.2 }
1024    
1025 sysadm 1.19 if (SYS_server_exit) // Do not save data on shutdown
1026     {
1027     goto cleanup;
1028     }
1029    
1030 sysadm 1.8 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 sysadm 1.16 ret = -1;
1043 sysadm 1.8 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 sysadm 1.24 // Calculate display length of content
1084     content_display_length = str_length(content, 1);
1085    
1086 sysadm 1.8 // 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 sysadm 1.13 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 sysadm 1.8 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 sysadm 1.13 free(content);
1115     content = NULL;
1116    
1117 sysadm 1.8 // Add content
1118 sysadm 1.13 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 sysadm 1.8 "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1128     content_f);
1129    
1130 sysadm 1.13 free(content_f);
1131     content_f = NULL;
1132    
1133     if (mysql_query(db, sql_content) != 0)
1134 sysadm 1.8 {
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 sysadm 1.13 free(sql_content);
1143     sql_content = NULL;
1144    
1145 sysadm 1.8 // 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 sysadm 1.29 "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', %d, %d, NOW(), 1, %d)",
1150 sysadm 1.11 p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1151     BBS_priv.uid, BBS_username, nickname_f, title_f,
1152 sysadm 1.29 p_article_new->cid, hostaddr_client,
1153     reply_note, BBS_user_exp, content_display_length);
1154 sysadm 1.8
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 sysadm 1.12 "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 sysadm 1.8
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 sysadm 1.28 // 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     "[hide]SYS_Reply_Article[/hide]有人回复了您所发表/回复的文章,快来"
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 sysadm 1.8 // 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 sysadm 1.17 mysql_close(db);
1272     db = NULL;
1273    
1274 sysadm 1.8 clearscr();
1275     moveto(1, 1);
1276 sysadm 1.24 prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1277 sysadm 1.8 press_any_key();
1278     ret = 1; // Success
1279 sysadm 1.2
1280 sysadm 1.6 cleanup:
1281 sysadm 1.8 mysql_free_result(rs);
1282     mysql_close(db);
1283    
1284     // Cleanup buffers
1285 sysadm 1.2 editor_data_cleanup(p_editor_data);
1286    
1287 sysadm 1.8 free(sql_content);
1288     free(content);
1289     free(content_f);
1290    
1291     return (int)ret;
1292 sysadm 1.1 }

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