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