]> granicus.if.org Git - cgit/blob - filter.c
config: make empty js= omit script tag
[cgit] / filter.c
1 /* filter.c: filter framework functions
2  *
3  * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
4  *
5  * Licensed under GNU General Public License v2
6  *   (see COPYING for full license text)
7  */
8
9 #include "cgit.h"
10 #include "html.h"
11 #ifndef NO_LUA
12 #include <dlfcn.h>
13 #include <lua.h>
14 #include <lualib.h>
15 #include <lauxlib.h>
16 #endif
17
18 static inline void reap_filter(struct cgit_filter *filter)
19 {
20         if (filter && filter->cleanup)
21                 filter->cleanup(filter);
22 }
23
24 void cgit_cleanup_filters(void)
25 {
26         int i;
27         reap_filter(ctx.cfg.about_filter);
28         reap_filter(ctx.cfg.commit_filter);
29         reap_filter(ctx.cfg.source_filter);
30         reap_filter(ctx.cfg.email_filter);
31         reap_filter(ctx.cfg.owner_filter);
32         reap_filter(ctx.cfg.auth_filter);
33         for (i = 0; i < cgit_repolist.count; ++i) {
34                 reap_filter(cgit_repolist.repos[i].about_filter);
35                 reap_filter(cgit_repolist.repos[i].commit_filter);
36                 reap_filter(cgit_repolist.repos[i].source_filter);
37                 reap_filter(cgit_repolist.repos[i].email_filter);
38                 reap_filter(cgit_repolist.repos[i].owner_filter);
39         }
40 }
41
42 static int open_exec_filter(struct cgit_filter *base, va_list ap)
43 {
44         struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
45         int pipe_fh[2];
46         int i;
47
48         for (i = 0; i < filter->base.argument_count; i++)
49                 filter->argv[i + 1] = va_arg(ap, char *);
50
51         filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
52                 "Unable to duplicate STDOUT");
53         chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
54         filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
55         if (filter->pid == 0) {
56                 close(pipe_fh[1]);
57                 chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
58                         "Unable to use pipe as STDIN");
59                 execvp(filter->cmd, filter->argv);
60                 die_errno("Unable to exec subprocess %s", filter->cmd);
61         }
62         close(pipe_fh[0]);
63         chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
64                 "Unable to use pipe as STDOUT");
65         close(pipe_fh[1]);
66         return 0;
67 }
68
69 static int close_exec_filter(struct cgit_filter *base)
70 {
71         struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
72         int i, exit_status = 0;
73
74         chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
75                 "Unable to restore STDOUT");
76         close(filter->old_stdout);
77         if (filter->pid < 0)
78                 goto done;
79         waitpid(filter->pid, &exit_status, 0);
80         if (WIFEXITED(exit_status))
81                 goto done;
82         die("Subprocess %s exited abnormally", filter->cmd);
83
84 done:
85         for (i = 0; i < filter->base.argument_count; i++)
86                 filter->argv[i + 1] = NULL;
87         return WEXITSTATUS(exit_status);
88
89 }
90
91 static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
92 {
93         struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
94         fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
95 }
96
97 static void cleanup_exec_filter(struct cgit_filter *base)
98 {
99         struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
100         if (filter->argv) {
101                 free(filter->argv);
102                 filter->argv = NULL;
103         }
104         if (filter->cmd) {
105                 free(filter->cmd);
106                 filter->cmd = NULL;
107         }
108 }
109
110 static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
111 {
112         struct cgit_exec_filter *f;
113         int args_size = 0;
114
115         f = xmalloc(sizeof(*f));
116         /* We leave argv for now and assign it below. */
117         cgit_exec_filter_init(f, xstrdup(cmd), NULL);
118         f->base.argument_count = argument_count;
119         args_size = (2 + argument_count) * sizeof(char *);
120         f->argv = xmalloc(args_size);
121         memset(f->argv, 0, args_size);
122         f->argv[0] = f->cmd;
123         return &f->base;
124 }
125
126 void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
127 {
128         memset(filter, 0, sizeof(*filter));
129         filter->base.open = open_exec_filter;
130         filter->base.close = close_exec_filter;
131         filter->base.fprintf = fprintf_exec_filter;
132         filter->base.cleanup = cleanup_exec_filter;
133         filter->cmd = cmd;
134         filter->argv = argv;
135         /* The argument count for open_filter is zero by default, unless called from new_filter, above. */
136         filter->base.argument_count = 0;
137 }
138
139 #ifdef NO_LUA
140 void cgit_init_filters(void)
141 {
142 }
143 #endif
144
145 #ifndef NO_LUA
146 static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
147 static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
148 static struct cgit_filter *current_write_filter = NULL;
149
150 void cgit_init_filters(void)
151 {
152         libc_write = dlsym(RTLD_NEXT, "write");
153         if (!libc_write)
154                 die("Could not locate libc's write function");
155 }
156
157 ssize_t write(int fd, const void *buf, size_t count)
158 {
159         if (fd != STDOUT_FILENO || !filter_write)
160                 return libc_write(fd, buf, count);
161         return filter_write(current_write_filter, buf, count);
162 }
163
164 static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
165 {
166         /* We want to avoid buggy nested patterns. */
167         assert(filter_write == NULL);
168         assert(current_write_filter == NULL);
169         current_write_filter = filter;
170         filter_write = new_write;
171 }
172
173 static inline void unhook_write(void)
174 {
175         assert(filter_write != NULL);
176         assert(current_write_filter != NULL);
177         filter_write = NULL;
178         current_write_filter = NULL;
179 }
180
181 struct lua_filter {
182         struct cgit_filter base;
183         char *script_file;
184         lua_State *lua_state;
185 };
186
187 static void error_lua_filter(struct lua_filter *filter)
188 {
189         die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1));
190         lua_pop(filter->lua_state, 1);
191 }
192
193 static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
194 {
195         struct lua_filter *filter = (struct lua_filter *)base;
196
197         lua_getglobal(filter->lua_state, "filter_write");
198         lua_pushlstring(filter->lua_state, buf, count);
199         if (lua_pcall(filter->lua_state, 1, 0, 0)) {
200                 error_lua_filter(filter);
201                 errno = EIO;
202                 return -1;
203         }
204         return count;
205 }
206
207 static inline int hook_lua_filter(lua_State *lua_state, void (*fn)(const char *txt))
208 {
209         const char *str;
210         ssize_t (*save_filter_write)(struct cgit_filter *base, const void *buf, size_t count);
211         struct cgit_filter *save_filter;
212
213         str = lua_tostring(lua_state, 1);
214         if (!str)
215                 return 0;
216
217         save_filter_write = filter_write;
218         save_filter = current_write_filter;
219         unhook_write();
220         fn(str);
221         hook_write(save_filter, save_filter_write);
222
223         return 0;
224 }
225
226 static int html_lua_filter(lua_State *lua_state)
227 {
228         return hook_lua_filter(lua_state, html);
229 }
230
231 static int html_txt_lua_filter(lua_State *lua_state)
232 {
233         return hook_lua_filter(lua_state, html_txt);
234 }
235
236 static int html_attr_lua_filter(lua_State *lua_state)
237 {
238         return hook_lua_filter(lua_state, html_attr);
239 }
240
241 static int html_url_path_lua_filter(lua_State *lua_state)
242 {
243         return hook_lua_filter(lua_state, html_url_path);
244 }
245
246 static int html_url_arg_lua_filter(lua_State *lua_state)
247 {
248         return hook_lua_filter(lua_state, html_url_arg);
249 }
250
251 static int html_include_lua_filter(lua_State *lua_state)
252 {
253         return hook_lua_filter(lua_state, (void (*)(const char *))html_include);
254 }
255
256 static void cleanup_lua_filter(struct cgit_filter *base)
257 {
258         struct lua_filter *filter = (struct lua_filter *)base;
259
260         if (!filter->lua_state)
261                 return;
262
263         lua_close(filter->lua_state);
264         filter->lua_state = NULL;
265         if (filter->script_file) {
266                 free(filter->script_file);
267                 filter->script_file = NULL;
268         }
269 }
270
271 static int init_lua_filter(struct lua_filter *filter)
272 {
273         if (filter->lua_state)
274                 return 0;
275
276         if (!(filter->lua_state = luaL_newstate()))
277                 return 1;
278
279         luaL_openlibs(filter->lua_state);
280
281         lua_pushcfunction(filter->lua_state, html_lua_filter);
282         lua_setglobal(filter->lua_state, "html");
283         lua_pushcfunction(filter->lua_state, html_txt_lua_filter);
284         lua_setglobal(filter->lua_state, "html_txt");
285         lua_pushcfunction(filter->lua_state, html_attr_lua_filter);
286         lua_setglobal(filter->lua_state, "html_attr");
287         lua_pushcfunction(filter->lua_state, html_url_path_lua_filter);
288         lua_setglobal(filter->lua_state, "html_url_path");
289         lua_pushcfunction(filter->lua_state, html_url_arg_lua_filter);
290         lua_setglobal(filter->lua_state, "html_url_arg");
291         lua_pushcfunction(filter->lua_state, html_include_lua_filter);
292         lua_setglobal(filter->lua_state, "html_include");
293
294         if (luaL_dofile(filter->lua_state, filter->script_file)) {
295                 error_lua_filter(filter);
296                 lua_close(filter->lua_state);
297                 filter->lua_state = NULL;
298                 return 1;
299         }
300         return 0;
301 }
302
303 static int open_lua_filter(struct cgit_filter *base, va_list ap)
304 {
305         struct lua_filter *filter = (struct lua_filter *)base;
306         int i;
307
308         if (init_lua_filter(filter))
309                 return 1;
310
311         hook_write(base, write_lua_filter);
312
313         lua_getglobal(filter->lua_state, "filter_open");
314         for (i = 0; i < filter->base.argument_count; ++i)
315                 lua_pushstring(filter->lua_state, va_arg(ap, char *));
316         if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) {
317                 error_lua_filter(filter);
318                 return 1;
319         }
320         return 0;
321 }
322
323 static int close_lua_filter(struct cgit_filter *base)
324 {
325         struct lua_filter *filter = (struct lua_filter *)base;
326         int ret = 0;
327
328         lua_getglobal(filter->lua_state, "filter_close");
329         if (lua_pcall(filter->lua_state, 0, 1, 0)) {
330                 error_lua_filter(filter);
331                 ret = -1;
332         } else {
333                 ret = lua_tonumber(filter->lua_state, -1);
334                 lua_pop(filter->lua_state, 1);
335         }
336
337         unhook_write();
338         return ret;
339 }
340
341 static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
342 {
343         struct lua_filter *filter = (struct lua_filter *)base;
344         fprintf(f, "%slua:%s\n", prefix, filter->script_file);
345 }
346
347
348 static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
349 {
350         struct lua_filter *filter;
351
352         filter = xmalloc(sizeof(*filter));
353         memset(filter, 0, sizeof(*filter));
354         filter->base.open = open_lua_filter;
355         filter->base.close = close_lua_filter;
356         filter->base.fprintf = fprintf_lua_filter;
357         filter->base.cleanup = cleanup_lua_filter;
358         filter->base.argument_count = argument_count;
359         filter->script_file = xstrdup(cmd);
360
361         return &filter->base;
362 }
363
364 #endif
365
366
367 int cgit_open_filter(struct cgit_filter *filter, ...)
368 {
369         int result;
370         va_list ap;
371         if (!filter)
372                 return 0;
373         va_start(ap, filter);
374         result = filter->open(filter, ap);
375         va_end(ap);
376         return result;
377 }
378
379 int cgit_close_filter(struct cgit_filter *filter)
380 {
381         if (!filter)
382                 return 0;
383         return filter->close(filter);
384 }
385
386 void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
387 {
388         filter->fprintf(filter, f, prefix);
389 }
390
391
392
393 static const struct {
394         const char *prefix;
395         struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
396 } filter_specs[] = {
397         { "exec", new_exec_filter },
398 #ifndef NO_LUA
399         { "lua", new_lua_filter },
400 #endif
401 };
402
403 struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
404 {
405         char *colon;
406         int i;
407         size_t len;
408         int argument_count;
409
410         if (!cmd || !cmd[0])
411                 return NULL;
412
413         colon = strchr(cmd, ':');
414         len = colon - cmd;
415         /*
416          * In case we're running on Windows, don't allow a single letter before
417          * the colon.
418          */
419         if (len == 1)
420                 colon = NULL;
421
422         switch (filtertype) {
423                 case AUTH:
424                         argument_count = 12;
425                         break;
426
427                 case EMAIL:
428                         argument_count = 2;
429                         break;
430
431                 case OWNER:
432                         argument_count = 0;
433                         break;
434
435                 case SOURCE:
436                 case ABOUT:
437                         argument_count = 1;
438                         break;
439
440                 case COMMIT:
441                 default:
442                         argument_count = 0;
443                         break;
444         }
445
446         /* If no prefix is given, exec filter is the default. */
447         if (!colon)
448                 return new_exec_filter(cmd, argument_count);
449
450         for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
451                 if (len == strlen(filter_specs[i].prefix) &&
452                     !strncmp(filter_specs[i].prefix, cmd, len))
453                         return filter_specs[i].ctor(colon + 1, argument_count);
454         }
455
456         die("Invalid filter type: %.*s", (int) len, cmd);
457 }