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

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