Mercurial > irccd
annotate plugins/links/links.c @ 1007:936cbd66b4b8
plugin links: fix a leak
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 16 Feb 2021 22:59:00 +0100 |
parents | 462e12e434fb |
children | a35537c50f09 |
rev | line source |
---|---|
987 | 1 /* |
2 * links.c -- links plugin | |
3 * | |
4 * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <assert.h> | |
20 #include <ctype.h> | |
21 #include <err.h> | |
22 #include <errno.h> | |
23 #include <regex.h> | |
24 #include <stdio.h> | |
25 #include <stdlib.h> | |
26 #include <string.h> | |
27 | |
28 #include <curl/curl.h> | |
29 | |
992
462e12e434fb
cmake: add CMake configuration
David Demelier <markand@malikania.fr>
parents:
990
diff
changeset
|
30 #include <irccd/compat.h> |
987 | 31 #include <irccd/irccd.h> |
32 #include <irccd/limits.h> | |
33 #include <irccd/server.h> | |
34 #include <irccd/subst.h> | |
35 #include <irccd/util.h> | |
36 | |
37 /* | |
38 * Since most websites are now bloated, we need a very large page size to | |
39 * analyse. Use 5MB for now. | |
40 */ | |
41 #define PAGE_MAX 5242880 | |
42 | |
43 struct req { | |
44 pthread_t thr; | |
45 CURL *curl; | |
46 struct irc_server *server; | |
47 int status; | |
48 char *link; | |
49 char *chan; | |
50 char *nickname; | |
51 char *origin; | |
52 FILE *fp; | |
53 char buf[]; | |
54 }; | |
55 | |
56 enum { | |
57 TPL_INFO | |
58 }; | |
59 | |
990
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
60 static unsigned long timeout = 30; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
61 |
987 | 62 static char templates[][512] = { |
1007
936cbd66b4b8
plugin links: fix a leak
David Demelier <markand@malikania.fr>
parents:
992
diff
changeset
|
63 [TPL_INFO] = "link from #{nickname}: #{title}" |
987 | 64 }; |
65 | |
66 static size_t | |
67 callback(char *ptr, size_t size, size_t nmemb, struct req *req) | |
68 { | |
69 fprintf(req->fp, "%.*s", (int)(size * nmemb), ptr); | |
70 | |
71 if (feof(req->fp) || ferror(req->fp)) | |
72 return 0; | |
73 | |
74 return size * nmemb; | |
75 } | |
76 | |
77 static const char * | |
78 parse(struct req *req) | |
79 { | |
80 regex_t regex; | |
81 regmatch_t match[2]; | |
82 char *ret = NULL; | |
83 | |
84 if (regcomp(®ex, "<title>([^<]+)<\\/title>", REG_EXTENDED | REG_ICASE) != 0) | |
85 return NULL; | |
86 | |
87 if (regexec(®ex, req->buf, 2, match, 0) == 0) { | |
88 ret = &req->buf[match[1].rm_so]; | |
89 ret[match[1].rm_eo - match[1].rm_so] = '\0'; | |
90 } | |
91 | |
92 regfree(®ex); | |
93 | |
94 return ret; | |
95 } | |
96 | |
97 static const char * | |
98 fmt(const struct req *req, const char *title) | |
99 { | |
100 static char line[IRC_MESSAGE_LEN]; | |
101 struct irc_subst subst = { | |
102 .time = time(NULL), | |
103 .flags = IRC_SUBST_DATE | IRC_SUBST_KEYWORDS | IRC_SUBST_IRC_ATTRS, | |
104 .keywords = (struct irc_subst_keyword []) { | |
105 { "channel", req->chan }, | |
106 { "nickname", req->nickname }, | |
107 { "origin", req->origin }, | |
108 { "server", req->server->name }, | |
109 { "title", title } | |
110 }, | |
111 .keywordsz = 5 | |
112 }; | |
113 | |
114 irc_subst(line, sizeof (line), templates[TPL_INFO], &subst); | |
115 | |
116 return line; | |
117 } | |
118 | |
119 static void | |
120 req_finish(struct req *); | |
121 | |
122 static void | |
123 complete(struct req *req) | |
124 { | |
125 const char *title; | |
126 | |
127 if (req->status && (title = parse(req))) | |
128 irc_server_message(req->server, req->chan, fmt(req, title)); | |
129 | |
130 pthread_join(req->thr, NULL); | |
131 req_finish(req); | |
132 } | |
133 | |
134 /* | |
135 * This function is running in a separate thread. | |
136 */ | |
137 static void * | |
138 routine(struct req *req) | |
139 { | |
140 typedef void (*func_t)(void *); | |
141 | |
142 if (curl_easy_perform(req->curl) == 0) | |
143 req->status = 1; | |
144 | |
145 irc_bot_post((func_t)complete, req); | |
146 | |
147 return NULL; | |
148 } | |
149 | |
150 static void | |
151 req_finish(struct req *req) | |
152 { | |
153 assert(req); | |
154 | |
155 if (req->server) | |
156 irc_server_decref(req->server); | |
157 if (req->curl) | |
158 curl_easy_cleanup(req->curl); | |
159 if (req->fp) | |
160 fclose(req->fp); | |
161 | |
1007
936cbd66b4b8
plugin links: fix a leak
David Demelier <markand@malikania.fr>
parents:
992
diff
changeset
|
162 free(req->link); |
987 | 163 free(req->chan); |
164 free(req->nickname); | |
165 free(req->origin); | |
166 free(req); | |
167 } | |
168 | |
169 static struct req * | |
170 req_new(struct irc_server *server, const char *origin, const char *channel, char *link) | |
171 { | |
172 assert(link); | |
173 | |
174 struct req *req; | |
175 struct irc_server_user user; | |
176 | |
177 if (!(req = calloc(1, sizeof (*req) + PAGE_MAX + 1))) { | |
178 free(link); | |
179 return NULL; | |
180 } | |
181 if (!(req->curl = curl_easy_init())) | |
182 goto enomem; | |
183 if (!(req->fp = fmemopen(req->buf, PAGE_MAX, "w"))) | |
184 goto enomem; | |
185 | |
186 irc_server_incref(server); | |
187 irc_server_split(origin, &user); | |
188 req->link = link; | |
189 req->server = server; | |
190 req->chan = strdup(channel); | |
191 req->nickname = strdup(user.nickname); | |
192 req->origin = strdup(origin); | |
193 | |
194 curl_easy_setopt(req->curl, CURLOPT_URL, link); | |
195 curl_easy_setopt(req->curl, CURLOPT_FOLLOWLOCATION, 1L); | |
196 curl_easy_setopt(req->curl, CURLOPT_WRITEFUNCTION, callback); | |
197 curl_easy_setopt(req->curl, CURLOPT_WRITEDATA, req); | |
990
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
198 curl_easy_setopt(req->curl, CURLOPT_TIMEOUT, timeout); |
987 | 199 |
200 return req; | |
201 | |
202 enomem: | |
203 errno = ENOMEM; | |
204 req_finish(req); | |
205 | |
206 return NULL; | |
207 } | |
208 | |
209 static void | |
210 req_start(struct req *req) | |
211 { | |
212 typedef void *(*func_t)(void *); | |
213 | |
214 if (pthread_create(&req->thr, NULL, (func_t)routine, req) != 0) | |
215 req_finish(req); | |
216 } | |
217 | |
218 void | |
219 links_event(const struct irc_event *ev) | |
220 { | |
221 struct req *req; | |
222 char *loc, *link, *end; | |
223 | |
224 if (ev->type != IRC_EVENT_MESSAGE) | |
225 return; | |
226 | |
227 /* Parse link. */ | |
228 if (!(loc = strstr(ev->message.message, "http://")) && | |
229 !(loc = strstr(ev->message.message, "https://"))) | |
230 return; | |
231 | |
232 /* Compute end to allocate only what's needed. */ | |
233 for (end = loc; *end && !isspace(*end); ) | |
234 ++end; | |
235 | |
236 link = irc_util_strndup(loc, end - loc); | |
237 | |
238 /* If the function fails, link is free'd anyway. */ | |
239 if (!(req = req_new(ev->server, ev->message.origin, ev->message.channel, link))) | |
240 return; | |
241 | |
242 req_start(req); | |
243 } | |
244 | |
245 void | |
246 links_set_template(const char *key, const char *value) | |
247 { | |
248 if (strcmp(key, "info") == 0) | |
249 strlcpy(templates[TPL_INFO], value, sizeof (templates[TPL_INFO])); | |
250 } | |
251 | |
252 const char * | |
253 links_get_template(const char *key) | |
254 { | |
255 if (strcmp(key, "info") == 0) | |
256 return templates[TPL_INFO]; | |
257 | |
258 return NULL; | |
259 } | |
260 | |
261 const char ** | |
262 links_get_templates(void) | |
263 { | |
264 static const char *keys[] = { "info", NULL }; | |
265 | |
266 return keys; | |
267 } | |
988
2e02fbb8b32b
irccd: load metadata from native plugins
David Demelier <markand@malikania.fr>
parents:
987
diff
changeset
|
268 |
990
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
269 void |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
270 links_set_option(const char *key, const char *value) |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
271 { |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
272 if (strcmp(key, "timeout") == 0) |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
273 timeout = atol(value); |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
274 } |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
275 |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
276 const char * |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
277 links_get_option(const char *key) |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
278 { |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
279 static char out[32]; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
280 |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
281 if (strcmp(key, "timeout") == 0) { |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
282 snprintf(out, sizeof (out), "%lu", timeout); |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
283 return out; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
284 } |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
285 |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
286 return NULL; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
287 } |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
288 |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
289 const char ** |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
290 links_get_options(void) |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
291 { |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
292 static const char *keys[] = { "timeout", NULL }; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
293 |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
294 return keys; |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
295 } |
daaf92c097e6
plugin links: add timeout support
David Demelier <markand@malikania.fr>
parents:
988
diff
changeset
|
296 |
988
2e02fbb8b32b
irccd: load metadata from native plugins
David Demelier <markand@malikania.fr>
parents:
987
diff
changeset
|
297 const char *links_description = "Parse links from messages"; |
2e02fbb8b32b
irccd: load metadata from native plugins
David Demelier <markand@malikania.fr>
parents:
987
diff
changeset
|
298 const char *links_version = IRCCD_VERSION; |
2e02fbb8b32b
irccd: load metadata from native plugins
David Demelier <markand@malikania.fr>
parents:
987
diff
changeset
|
299 const char *links_license = "ISC"; |
2e02fbb8b32b
irccd: load metadata from native plugins
David Demelier <markand@malikania.fr>
parents:
987
diff
changeset
|
300 const char *links_author = "David Demelier <markand@malikania.fr>"; |