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