/[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.26 - (hide annotations)
Sun Oct 5 05:00:50 2025 UTC (5 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.25: +1 -1 lines
Content type: text/x-csrc
Rename lml_plain() to lml_render()

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.24 len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN, DOECHO);
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.3
734     if (p_section == NULL || p_article == NULL)
735     {
736     log_error("NULL pointer error\n");
737     }
738    
739 sysadm 1.12 if (!checkpriv(&BBS_priv, p_section->sid, S_POST))
740     {
741     clearscr();
742     moveto(1, 1);
743 sysadm 1.24 prints("您没有权限在本版块发表文章\n");
744 sysadm 1.12 press_any_key();
745    
746     return 0;
747     }
748    
749 sysadm 1.8 p_article_new->title[0] = '\0';
750 sysadm 1.6 snprintf(title_input, sizeof(title_input), "Re: %s", p_article->title);
751 sysadm 1.20 len = split_line(title_input, TITLE_INPUT_MAX_LEN, &eol, &display_len, 0);
752 sysadm 1.6 title_input[len] = '\0';
753    
754     db = db_open();
755     if (db == NULL)
756     {
757     log_error("db_open() error: %s\n", mysql_error(db));
758 sysadm 1.16 ret = -1;
759     goto cleanup;
760 sysadm 1.6 }
761    
762     snprintf(sql, sizeof(sql),
763 sysadm 1.14 "SELECT `lock` FROM bbs WHERE AID = %d",
764     (p_article->tid == 0 ? p_article->aid : p_article->tid));
765    
766     if (mysql_query(db, sql) != 0)
767     {
768     log_error("Query article status error: %s\n", mysql_error(db));
769 sysadm 1.16 ret = -1;
770     goto cleanup;
771 sysadm 1.14 }
772     if ((rs = mysql_store_result(db)) == NULL)
773     {
774     log_error("Get article status data failed\n");
775 sysadm 1.16 ret = -1;
776     goto cleanup;
777 sysadm 1.14 }
778    
779     if ((row = mysql_fetch_row(rs)))
780     {
781     if (atoi(row[0]) != 0)
782     {
783     topic_locked = 1;
784     }
785     }
786     mysql_free_result(rs);
787     rs = NULL;
788    
789     if (topic_locked) // Reply is not allowed
790     {
791 sysadm 1.17 mysql_close(db);
792     db = NULL;
793 sysadm 1.18
794 sysadm 1.14 clearscr();
795     moveto(1, 1);
796 sysadm 1.24 prints("该主题谢绝回复");
797 sysadm 1.14 press_any_key();
798    
799     goto cleanup;
800     }
801    
802     snprintf(sql, sizeof(sql),
803 sysadm 1.6 "SELECT bbs_content.CID, bbs_content.content "
804     "FROM bbs INNER JOIN bbs_content ON bbs.CID = bbs_content.CID "
805     "WHERE bbs.AID = %d",
806     p_article->aid);
807    
808     if (mysql_query(db, sql) != 0)
809 sysadm 1.3 {
810 sysadm 1.6 log_error("Query article content error: %s\n", mysql_error(db));
811 sysadm 1.16 ret = -1;
812     goto cleanup;
813 sysadm 1.6 }
814     if ((rs = mysql_use_result(db)) == NULL)
815     {
816     log_error("Get article content data failed\n");
817 sysadm 1.16 ret = -1;
818     goto cleanup;
819 sysadm 1.3 }
820 sysadm 1.5
821 sysadm 1.6 if ((row = mysql_fetch_row(rs)))
822     {
823 sysadm 1.8 content = malloc(ARTICLE_CONTENT_MAX_LEN);
824     if (content == NULL)
825     {
826     log_error("malloc(content) error: OOM\n");
827     ret = -1;
828     goto cleanup;
829     }
830    
831     content_f = malloc(ARTICLE_CONTENT_MAX_LEN);
832     if (content_f == NULL)
833     {
834     log_error("malloc(content_f) error: OOM\n");
835     ret = -1;
836     goto cleanup;
837     }
838    
839 sysadm 1.6 // Apply LML render to content body
840 sysadm 1.26 len = lml_render(row[1], content_f, ARTICLE_CONTENT_MAX_LEN, 0);
841 sysadm 1.6 content_f[len] = '\0';
842    
843 sysadm 1.7 // Remove control sequence
844 sysadm 1.20 len = str_filter(content_f, 0);
845 sysadm 1.7
846 sysadm 1.8 len = snprintf(content, ARTICLE_CONTENT_MAX_LEN,
847 sysadm 1.24 "\n\n【 在 %s (%s) 的大作中提到: 】\n",
848 sysadm 1.8 p_article->username, p_article->nickname);
849 sysadm 1.7
850 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);
851 sysadm 1.7 for (i = 0; i < quote_content_lines; i++)
852 sysadm 1.6 {
853     memcpy(content + len, ": ", 2); // quote line prefix
854     len += 2;
855     memcpy(content + len, content_f + line_offsets[i], (size_t)(line_offsets[i + 1] - line_offsets[i]));
856     len += (line_offsets[i + 1] - line_offsets[i]);
857 sysadm 1.18 if (content[len - 1] != '\n') // Appennd \n if not exist
858     {
859     content[len] = '\n';
860     len++;
861     }
862 sysadm 1.6 }
863 sysadm 1.9 if (content[len - 1] != '\n') // Appennd \n if not exist
864     {
865     content[len] = '\n';
866     len++;
867     }
868 sysadm 1.6 content[len] = '\0';
869 sysadm 1.8
870     free(content_f);
871     content_f = NULL;
872    
873     p_editor_data = editor_data_load(content);
874     if (p_editor_data == NULL)
875     {
876     log_error("editor_data_load(aid=%d, cid=%d) error\n", p_article->aid, atoi(row[0]));
877 sysadm 1.16 ret = -1;
878 sysadm 1.8 goto cleanup;
879     }
880    
881     free(content);
882     content = NULL;
883 sysadm 1.6 }
884     mysql_free_result(rs);
885 sysadm 1.8 rs = NULL;
886    
887 sysadm 1.6 mysql_close(db);
888 sysadm 1.8 db = NULL;
889 sysadm 1.5
890 sysadm 1.6 // Set title and sign
891     for (ch = 'T'; !SYS_server_exit;)
892 sysadm 1.3 {
893 sysadm 1.6 clearscr();
894     moveto(21, 1);
895 sysadm 1.24 prints("回复文章于 %s[%s] 讨论区", p_section->stitle, p_section->sname);
896 sysadm 1.6 moveto(22, 1);
897 sysadm 1.24 prints("标题: %s", (p_article_new->title[0] == '\0' ? "[无]" : p_article_new->title));
898 sysadm 1.6 moveto(23, 1);
899 sysadm 1.24 prints("使用第 %d 个签名", sign_id);
900 sysadm 1.6
901     if (toupper(ch) != 'T')
902     {
903 sysadm 1.24 prints(" 按0~3选签名档(0表示不使用)");
904 sysadm 1.6
905     moveto(24, 1);
906 sysadm 1.24 prints("T改标题, C取消, Enter继续: ");
907 sysadm 1.6 iflush();
908     ch = 0;
909     }
910    
911     for (; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
912     {
913     switch (toupper(ch))
914     {
915 sysadm 1.19 case KEY_NULL:
916     case KEY_TIMEOUT:
917     goto cleanup;
918 sysadm 1.6 case CR:
919     igetch_reset();
920     break;
921     case 'T':
922     moveto(24, 1);
923     clrtoeol();
924 sysadm 1.24 len = get_data(24, 1, "标题: ", title_input, sizeof(title_input), TITLE_INPUT_MAX_LEN, DOECHO);
925 sysadm 1.6 for (p = title_input; *p == ' '; p++)
926     ;
927     for (q = title_input + len; q > p && *(q - 1) == ' '; q--)
928     ;
929     *q = '\0';
930     len = q - p;
931     if (*p != '\0')
932     {
933 sysadm 1.8 memcpy(p_article_new->title, p, (size_t)len + 1);
934     memcpy(title_input, p_article_new->title, (size_t)len + 1);
935 sysadm 1.6 }
936     ch = 0;
937     break;
938     case 'C':
939     clearscr();
940     moveto(1, 1);
941 sysadm 1.24 prints("取消...");
942 sysadm 1.6 press_any_key();
943     goto cleanup;
944     case '0':
945     case '1':
946     case '2':
947     case '3':
948     sign_id = ch - '0';
949     break;
950     default: // Invalid selection
951     continue;
952     }
953    
954     break;
955     }
956    
957 sysadm 1.8 if (ch != CR || p_article_new->title[0] == '\0')
958 sysadm 1.6 {
959     continue;
960     }
961    
962     for (ch = 'E'; !SYS_server_exit && toupper(ch) == 'E';)
963     {
964     editor_display(p_editor_data);
965    
966     clearscr();
967     moveto(1, 1);
968 sysadm 1.24 prints("(S)发送, (C)取消, (T)更改标题 or (E)再编辑? [S]: ");
969 sysadm 1.6 iflush();
970    
971     for (ch = 0; !SYS_server_exit; ch = igetch_t(MAX_DELAY_TIME))
972     {
973     switch (toupper(ch))
974     {
975 sysadm 1.19 case KEY_NULL:
976     case KEY_TIMEOUT:
977     goto cleanup;
978 sysadm 1.6 case CR:
979     igetch_reset();
980     case 'S':
981     break;
982     case 'C':
983     clearscr();
984     moveto(1, 1);
985 sysadm 1.24 prints("取消...");
986 sysadm 1.6 press_any_key();
987     goto cleanup;
988     case 'T':
989     break;
990     case 'E':
991     break;
992     default: // Invalid selection
993     continue;
994     }
995    
996     break;
997     }
998     }
999    
1000     if (toupper(ch) != 'T')
1001     {
1002     break;
1003     }
1004 sysadm 1.2 }
1005    
1006 sysadm 1.19 if (SYS_server_exit) // Do not save data on shutdown
1007     {
1008     goto cleanup;
1009     }
1010    
1011 sysadm 1.8 content = malloc(ARTICLE_CONTENT_MAX_LEN);
1012     if (content == NULL)
1013     {
1014     log_error("malloc(content) error: OOM\n");
1015     ret = -1;
1016     goto cleanup;
1017     }
1018    
1019     len_content = editor_data_save(p_editor_data, content, ARTICLE_CONTENT_MAX_LEN);
1020     if (len_content < 0)
1021     {
1022     log_error("editor_data_save() error\n");
1023 sysadm 1.16 ret = -1;
1024 sysadm 1.8 goto cleanup;
1025     }
1026    
1027     db = db_open();
1028     if (db == NULL)
1029     {
1030     log_error("db_open() error: %s\n", mysql_error(db));
1031     ret = -1;
1032     goto cleanup;
1033     }
1034    
1035     if (sign_id > 0)
1036     {
1037     snprintf(sql, sizeof(sql),
1038     "SELECT sign_%d AS sign FROM user_pubinfo WHERE UID = %d",
1039     sign_id, BBS_priv.uid);
1040    
1041     if (mysql_query(db, sql) != 0)
1042     {
1043     log_error("Query sign error: %s\n", mysql_error(db));
1044     ret = -1;
1045     goto cleanup;
1046     }
1047     if ((rs = mysql_use_result(db)) == NULL)
1048     {
1049     log_error("Get sign data failed\n");
1050     ret = -1;
1051     goto cleanup;
1052     }
1053    
1054     if ((row = mysql_fetch_row(rs)))
1055     {
1056     len_content += snprintf(content + len_content,
1057     ARTICLE_CONTENT_MAX_LEN - (size_t)len_content,
1058     "\n\n--\n%s\n", row[0]);
1059     }
1060     mysql_free_result(rs);
1061     rs = NULL;
1062     }
1063    
1064 sysadm 1.24 // Calculate display length of content
1065     content_display_length = str_length(content, 1);
1066    
1067 sysadm 1.8 // Begin transaction
1068     if (mysql_query(db, "SET autocommit=0") != 0)
1069     {
1070     log_error("SET autocommit=0 error: %s\n", mysql_error(db));
1071     ret = -1;
1072     goto cleanup;
1073     }
1074    
1075     if (mysql_query(db, "BEGIN") != 0)
1076     {
1077     log_error("Begin transaction error: %s\n", mysql_error(db));
1078     ret = -1;
1079     goto cleanup;
1080     }
1081    
1082     // Secure SQL parameters
1083 sysadm 1.13 content_f = malloc((size_t)len_content * 2 + 1);
1084     if (content_f == NULL)
1085     {
1086     log_error("malloc(content_f) error: OOM\n");
1087     ret = -1;
1088     goto cleanup;
1089     }
1090    
1091 sysadm 1.8 mysql_real_escape_string(db, nickname_f, BBS_nickname, (unsigned long)strnlen(BBS_nickname, sizeof(BBS_nickname)));
1092     mysql_real_escape_string(db, title_f, p_article_new->title, strnlen(p_article_new->title, sizeof(p_article_new->title)));
1093     mysql_real_escape_string(db, content_f, content, (unsigned long)len_content);
1094    
1095 sysadm 1.13 free(content);
1096     content = NULL;
1097    
1098 sysadm 1.8 // Add content
1099 sysadm 1.13 sql_content = malloc(SQL_BUFFER_LEN + (size_t)len_content * 2 + 1);
1100     if (sql_content == NULL)
1101     {
1102     log_error("malloc(sql_content) error: OOM\n");
1103     ret = -1;
1104     goto cleanup;
1105     }
1106    
1107     snprintf(sql_content, SQL_BUFFER_LEN + (size_t)len_content * 2 + 1,
1108 sysadm 1.8 "INSERT INTO bbs_content(AID, content) values(0, '%s')",
1109     content_f);
1110    
1111 sysadm 1.13 free(content_f);
1112     content_f = NULL;
1113    
1114     if (mysql_query(db, sql_content) != 0)
1115 sysadm 1.8 {
1116     log_error("Add article content error: %s\n", mysql_error(db));
1117     ret = -1;
1118     goto cleanup;
1119     }
1120    
1121     p_article_new->cid = (int32_t)mysql_insert_id(db);
1122    
1123 sysadm 1.13 free(sql_content);
1124     sql_content = NULL;
1125    
1126 sysadm 1.8 // Add article
1127     snprintf(sql, sizeof(sql),
1128     "INSERT INTO bbs(SID, TID, UID, username, nickname, title, CID, transship, "
1129     "sub_dt, sub_ip, reply_note, exp, last_reply_dt, icon, length) "
1130 sysadm 1.24 "VALUES(%d, %d, %d, '%s', '%s', '%s', %d, 0, NOW(), '%s', 1, %d, NOW(), 1, %d)",
1131 sysadm 1.11 p_section->sid, (p_article->tid == 0 ? p_article->aid : p_article->tid),
1132     BBS_priv.uid, BBS_username, nickname_f, title_f,
1133 sysadm 1.24 p_article_new->cid, hostaddr_client, BBS_user_exp, content_display_length);
1134 sysadm 1.8
1135     if (mysql_query(db, sql) != 0)
1136     {
1137     log_error("Add article error: %s\n", mysql_error(db));
1138     ret = -1;
1139     goto cleanup;
1140     }
1141    
1142     p_article_new->aid = (int32_t)mysql_insert_id(db);
1143    
1144     // Update topic article
1145     snprintf(sql, sizeof(sql),
1146     "UPDATE bbs SET reply_count = reply_count + 1, "
1147     "last_reply_dt = NOW(), last_reply_UID=%d, last_reply_username = '%s', "
1148 sysadm 1.12 "last_reply_nickname = '%s' WHERE AID = %d",
1149     BBS_priv.uid, BBS_username, nickname_f,
1150     (p_article->tid == 0 ? p_article->aid : p_article->tid));
1151    
1152     if (mysql_query(db, sql) != 0)
1153     {
1154     log_error("Update topic article error: %s\n", mysql_error(db));
1155     ret = -1;
1156     goto cleanup;
1157     }
1158 sysadm 1.8
1159     // Link content to article
1160     snprintf(sql, sizeof(sql),
1161     "UPDATE bbs_content SET AID = %d WHERE CID = %d",
1162     p_article_new->aid, p_article_new->cid);
1163    
1164     if (mysql_query(db, sql) != 0)
1165     {
1166     log_error("Update content error: %s\n", mysql_error(db));
1167     ret = -1;
1168     goto cleanup;
1169     }
1170    
1171     // Add exp
1172     if (checkpriv(&BBS_priv, p_section->sid, S_GETEXP)) // Except in test section
1173     {
1174     snprintf(sql, sizeof(sql),
1175     "UPDATE user_pubinfo SET exp = exp + %d WHERE UID = %d",
1176     3, BBS_priv.uid);
1177    
1178     if (mysql_query(db, sql) != 0)
1179     {
1180     log_error("Update exp error: %s\n", mysql_error(db));
1181     ret = -1;
1182     goto cleanup;
1183     }
1184     }
1185    
1186     // Add log
1187     snprintf(sql, sizeof(sql),
1188     "INSERT INTO bbs_article_op(AID, UID, type, op_dt, op_ip)"
1189     "VALUES(%d, %d, 'A', NOW(), '%s')",
1190     p_article_new->aid, BBS_priv.uid, hostaddr_client);
1191    
1192     if (mysql_query(db, sql) != 0)
1193     {
1194     log_error("Add log error: %s\n", mysql_error(db));
1195     ret = -1;
1196     goto cleanup;
1197     }
1198    
1199     // Commit transaction
1200     if (mysql_query(db, "COMMIT") != 0)
1201     {
1202     log_error("Commit transaction error: %s\n", mysql_error(db));
1203     ret = -1;
1204     goto cleanup;
1205     }
1206    
1207 sysadm 1.17 mysql_close(db);
1208     db = NULL;
1209    
1210 sysadm 1.8 clearscr();
1211     moveto(1, 1);
1212 sysadm 1.24 prints("发送完成,新文章通常会在%d秒后可见", BBS_section_list_load_interval);
1213 sysadm 1.8 press_any_key();
1214     ret = 1; // Success
1215 sysadm 1.2
1216 sysadm 1.6 cleanup:
1217 sysadm 1.8 mysql_free_result(rs);
1218     mysql_close(db);
1219    
1220     // Cleanup buffers
1221 sysadm 1.2 editor_data_cleanup(p_editor_data);
1222    
1223 sysadm 1.8 free(sql_content);
1224     free(content);
1225     free(content_f);
1226    
1227     return (int)ret;
1228 sysadm 1.1 }

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