comparison plugins/links/links.c @ 987:685b85367c8e

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