1 /* Copyright (C) 2016-2017 Z.Wind.L <zwindl@protonmail.com>
2 * Copyright (C) 2016-2019 Shengyu Zhang <i@silverrainz.me>
3 *
4 * This file is part of Srain.
5 *
6 * Srain is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "core/core.h"
20 #include "markup_renderer.h"
21 #include "pattern_set.h"
22
23 #include "./renderer.h"
24
25 #define PATTERNS_KEY "pattern_render_module_patterns"
26
27 static void init(void);
28 static void finalize(void);
29 static SrnRet render(SrnMessage *msg);
30 static GList** alloc_patterns();
31 static void free_patterns(GList **patterns);
32 static GList* get_patterns(SrnMessage *msg);
33 static void text(GMarkupParseContext *context, const gchar *text,
34 gsize text_len, gpointer user_data, GError **error);
35
36 static SrnMarkupRenderer *markup_renderer;
37
38 /**
39 * @brief pattern_renderer is a render module for extracting text from message
40 * content via given pattern, and use them as new message content.
41 *
42 * NOTE: Make sure pattern_renderer is executed as first renderer.
43 */
44 SrnMessageRenderer pattern_renderer = {
45 .name = "pattern",
46 .init = init,
47 .finalize = finalize,
48 .render = render,
49 };
50
init(void)51 void init(void) {
52 GMarkupParser *parser;
53
54 markup_renderer = srn_markup_renderer_new();
55 parser = srn_markup_renderer_get_markup_parser(markup_renderer);
56 parser->start_element = NULL;
57 parser->end_element = NULL;
58 parser->text = text;
59 }
60
finalize(void)61 void finalize(void) {
62 srn_markup_renderer_free(markup_renderer);
63 }
64
render(SrnMessage * msg)65 static SrnRet render(SrnMessage *msg) {
66 char *raw_content;
67 GList *patterns;
68 GList *lst;
69 SrnRet ret;
70 SrnPatternSet *pattern_set;
71
72 pattern_set = srn_application_get_default()->pattern_set;
73 g_return_val_if_fail(pattern_set, SRN_ERR);
74
75 patterns = get_patterns(msg);
76
77 raw_content = NULL;
78 ret = srn_markup_renderer_render(markup_renderer,
79 msg->rendered_content, &raw_content, NULL);
80 if (!RET_IS_OK(ret)){
81 // ret = RET_ERR(_("Failed to render markup text: %1$s"), RET_MSG(ret));
82 // return TRUE;
83 }
84 lst = patterns;
85 while (lst) {
86 const char *pattern;
87 const GRegex *regex;
88
89 pattern = lst->data;
90 regex = srn_pattern_set_get(pattern_set, pattern);
91 if (regex) {
92 GMatchInfo *match_info;
93
94 match_info = NULL;
95 g_regex_match(regex, raw_content, 0, &match_info);
96 if (g_match_info_matches(match_info)) {
97 char *sender;
98 char *content;
99 char *time;
100
101 sender = g_match_info_fetch_named(match_info, "sender");
102 content = g_match_info_fetch_named(match_info, "content");
103 time = g_match_info_fetch_named(match_info, "time");
104
105 if (sender) {
106 g_free(msg->rendered_remark);
107 msg->rendered_remark = msg->rendered_sender;
108 msg->rendered_sender = g_markup_escape_text(sender, -1);
109 }
110 if (content) {
111 g_free(msg->rendered_content);
112 msg->rendered_content = g_markup_escape_text(content, -1);
113 }
114 if (time) {
115 g_free(msg->rendered_short_time);
116 msg->rendered_short_time = g_markup_escape_text(time, -1);
117 }
118
119 g_free(sender);
120 g_free(content);
121 g_free(time);
122 g_match_info_free(match_info);
123 }
124 }
125 lst = g_list_next(lst);
126 }
127
128 g_list_free(patterns);
129
130 return SRN_OK;
131 }
132
133 /**
134 * @brief srn_render_attach_pattern attach a pattern name to given SrnExtraData.
135 * The attached pattern name will be used to render message.
136 *
137 * @param extra_data
138 * @param pattern is name of pattern which can be found i
139 * SrnApplication->pattern_set.
140 *
141 * @return
142 */
srn_render_attach_pattern(SrnExtraData * extra_data,const char * pattern)143 SrnRet srn_render_attach_pattern(SrnExtraData *extra_data, const char *pattern){
144 GList **patterns;
145 GList *lst;
146
147 patterns = srn_extra_data_get(extra_data, PATTERNS_KEY);
148 if (!patterns) {
149 patterns = alloc_patterns();
150 srn_extra_data_set(extra_data, PATTERNS_KEY, patterns,
151 (GDestroyNotify)free_patterns);
152 }
153
154 lst = *patterns;
155 while (lst) {
156 if (g_strcasecmp(lst->data, pattern) == 0) {
157 return SRN_ERR;
158 }
159 lst = g_list_next(lst);
160 }
161
162 *patterns = g_list_append(*patterns, g_strdup(pattern));
163
164 return SRN_OK;
165 }
166
srn_render_detach_pattern(SrnExtraData * extra_data,const char * pattern)167 SrnRet srn_render_detach_pattern(SrnExtraData *extra_data, const char *pattern){
168 GList **patterns;
169 GList *lst;
170
171 patterns = srn_extra_data_get(extra_data, PATTERNS_KEY);
172 g_return_val_if_fail(patterns, SRN_OK);
173
174 lst = *patterns;
175 while (lst) {
176 if (g_strcasecmp(lst->data, pattern) == 0) {
177 break;
178 }
179 lst = g_list_next(lst);
180 }
181 if (!lst) {
182 return SRN_ERR;
183 }
184
185 g_free(lst->data);
186 *patterns = g_list_delete_link(*patterns, lst);
187
188 return SRN_OK;
189 }
190
alloc_patterns()191 static GList** alloc_patterns() {
192 return g_malloc0(sizeof(GList **));
193 }
194
free_patterns(GList ** patterns)195 static void free_patterns(GList **patterns){
196 if (patterns) {
197 if (*patterns) {
198 g_list_free_full(*patterns, g_free);
199 }
200 g_free(patterns);
201 }
202 }
203
204 /**
205 * @brief get_patterns
206 *
207 * @return a list which contains const string, should be freed by g_list_free();
208 */
get_patterns(SrnMessage * msg)209 static GList* get_patterns(SrnMessage *msg) {
210 GList *iter;
211 GList *datas; // List of SrnExtraData
212 GList *patterns;
213
214 datas = NULL;
215 datas = g_list_append(datas, msg->sender->extra_data);
216 datas = g_list_append(datas, msg->sender->srv_user->extra_data);
217 datas = g_list_append(datas, msg->chat->extra_data);
218 datas = g_list_append(datas, msg->chat->srv->chat->extra_data);
219
220 patterns = NULL;
221 iter = datas;
222 while (iter) {
223 SrnExtraData *data;
224 GList **lp;
225
226 data = iter->data;
227 lp = srn_extra_data_get(data, PATTERNS_KEY);
228 if (lp && *lp) {
229 patterns = g_list_concat(patterns, g_list_copy(*lp));
230 }
231 iter = g_list_next(iter);
232 }
233
234 g_list_free(datas);
235
236 return patterns;
237 }
238
text(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)239 static void text(GMarkupParseContext *context, const gchar *text,
240 gsize text_len, gpointer user_data, GError **error){
241 g_string_append_len(srn_markup_renderer_get_markup(user_data), text, text_len);
242 }
243