aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld2014-01-13 14:18:51 +0100
committerJason A. Donenfeld2014-01-14 02:00:07 +0100
commitf43b228d0bca5791be98ff3fbb2f8743219635b6 (patch)
tree2200619d48fd24f5e809736ff94c84a57da4481f
parente83b51b4f6bd53efea0c772e6ecdf1c5605ca611 (diff)
downloadcgit-f43b228d0bca5791be98ff3fbb2f8743219635b6.tar.gz
cgit-f43b228d0bca5791be98ff3fbb2f8743219635b6.tar.bz2
cgit-f43b228d0bca5791be98ff3fbb2f8743219635b6.zip
filter: add lua support
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--README1
-rw-r--r--cgit.mk22
-rw-r--r--cgitrc.5.txt29
-rw-r--r--filter.c186
4 files changed, 235 insertions, 3 deletions
diff --git a/README b/README
index e1d5262..d79cd43 100644
--- a/README
+++ b/README
@@ -38,6 +38,7 @@ Dependencies
38* libzip 38* libzip
39* libcrypto (OpenSSL) 39* libcrypto (OpenSSL)
40* libssl (OpenSSL) 40* libssl (OpenSSL)
41* optional: luajit or lua
41 42
42Apache configuration 43Apache configuration
43-------------------- 44--------------------
diff --git a/cgit.mk b/cgit.mk
index 9d6dea8..25f2eab 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -25,6 +25,25 @@ ifdef NO_C99_FORMAT
25 CFLAGS += -DNO_C99_FORMAT 25 CFLAGS += -DNO_C99_FORMAT
26endif 26endif
27 27
28ifdef NO_LUA
29 CFLAGS += -DNO_LUA
30else
31
32ifeq (VANILLA,$(LUA_IMPLEMENTATION))
33 CFLAGS += -llua
34else
35 LUAJIT_LIBS := $(shell pkg-config --libs luajit)
36 LUAJIT_CFLAGS := $(shell pkg-config --cflags luajit)
37 CGIT_LIBS += $(LUAJIT_LIBS)
38 CFLAGS += $(LUAJIT_CFLAGS)
39endif
40
41endif
42
43CGIT_LIBS += -ldl
44
45
46
28CGIT_OBJ_NAMES += cgit.o 47CGIT_OBJ_NAMES += cgit.o
29CGIT_OBJ_NAMES += cache.o 48CGIT_OBJ_NAMES += cache.o
30CGIT_OBJ_NAMES += cmd.o 49CGIT_OBJ_NAMES += cmd.o
@@ -61,9 +80,6 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
61$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \ 80$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
62 -DCGIT_VERSION='"$(CGIT_VERSION)"' 81 -DCGIT_VERSION='"$(CGIT_VERSION)"'
63 82
64CGIT_LIBS += -ldl
65
66
67# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not 83# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
68# handled by that and we must handle them ourselves. 84# handled by that and we must handle them ourselves.
69cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d) 85cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d)
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 60159f6..78f33c8 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -564,6 +564,35 @@ specification with the relevant string; available values are:
564'exec:':: 564'exec:'::
565 The default "one process per filter" mode. 565 The default "one process per filter" mode.
566 566
567'lua:'::
568 Executes the script using a built-in Lua interpreter. The script is
569 loaded once per execution of cgit, and may be called multiple times
570 during cgit's lifetime, making it a good choice for repeated filters
571 such as the 'email filter'. It responds to three functions:
572
573 'filter_open(argument1, argument2, argument3, ...)'::
574 This is called upon activation of the filter for a particular
575 set of data.
576 'filter_write(buffer)'::
577 This is called whenever cgit writes data to the webpage.
578 'filter_close()'::
579 This is called when the current filtering operation is
580 completed.
581
582 Additionally, cgit exposes to the Lua the following built-in functions:
583
584 'html(str)'::
585 Writes 'str' to the webpage.
586 'html_txt(str)'::
587 HTML escapes and writes 'str' to the webpage.
588 'html_attr(str)'::
589 HTML escapes for an attribute and writes "str' to the webpage.
590 'html_url_path(str)'::
591 URL escapes for a path and writes 'str' to the webpage.
592 'html_url_arg(str)'::
593 URL escapes for an argument and writes 'str' to the webpage.
594
595
567Parameters are provided to filters as follows. 596Parameters are provided to filters as follows.
568 597
569about filter:: 598about filter::
diff --git a/filter.c b/filter.c
index f5a5992..3702585 100644
--- a/filter.c
+++ b/filter.c
@@ -7,12 +7,19 @@
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h"
10#include <sys/types.h> 11#include <sys/types.h>
11#include <sys/wait.h> 12#include <sys/wait.h>
12#include <unistd.h> 13#include <unistd.h>
13#include <string.h> 14#include <string.h>
14#include <stdlib.h> 15#include <stdlib.h>
15#include <dlfcn.h> 16#include <dlfcn.h>
17#include <errno.h>
18#ifndef NO_LUA
19#include <lua.h>
20#include <lualib.h>
21#include <lauxlib.h>
22#endif
16 23
17static ssize_t (*libc_write)(int fd, const void *buf, size_t count); 24static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
18static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL; 25static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
@@ -164,6 +171,182 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
164 filter->base.argument_count = 0; 171 filter->base.argument_count = 0;
165} 172}
166 173
174#ifndef NO_LUA
175struct lua_filter {
176 struct cgit_filter base;
177 char *script_file;
178 lua_State *lua_state;
179};
180
181static void error_lua_filter(struct lua_filter *filter)
182{
183 die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1));
184 lua_pop(filter->lua_state, 1);
185}
186
187static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
188{
189 struct lua_filter *filter = (struct lua_filter *) base;
190
191 lua_getglobal(filter->lua_state, "filter_write");
192 lua_pushlstring(filter->lua_state, buf, count);
193 if (lua_pcall(filter->lua_state, 1, 0, 0)) {
194 error_lua_filter(filter);
195 errno = EIO;
196 return -1;
197 }
198 return count;
199}
200
201static inline int hook_lua_filter(lua_State *lua_state, void (*fn)(const char *txt))
202{
203 const char *str;
204 ssize_t (*save_filter_write)(struct cgit_filter *base, const void *buf, size_t count);
205 struct cgit_filter *save_filter;
206
207 str = lua_tostring(lua_state, 1);
208 if (!str)
209 return 0;
210
211 save_filter_write = filter_write;
212 save_filter = current_write_filter;
213 unhook_write();
214 fn(str);
215 hook_write(save_filter, save_filter_write);
216
217 return 0;
218}
219
220static int html_lua_filter(lua_State *lua_state)
221{
222 return hook_lua_filter(lua_state, html);
223}
224
225static int html_txt_lua_filter(lua_State *lua_state)
226{
227 return hook_lua_filter(lua_state, html_txt);
228}
229
230static int html_attr_lua_filter(lua_State *lua_state)
231{
232 return hook_lua_filter(lua_state, html_attr);
233}
234
235static int html_url_path_lua_filter(lua_State *lua_state)
236{
237 return hook_lua_filter(lua_state, html_url_path);
238}
239
240static int html_url_arg_lua_filter(lua_State *lua_state)
241{
242 return hook_lua_filter(lua_state, html_url_arg);
243}
244
245static void cleanup_lua_filter(struct cgit_filter *base)
246{
247 struct lua_filter *filter = (struct lua_filter *) base;
248
249 if (!filter->lua_state)
250 return;
251
252 lua_close(filter->lua_state);
253 filter->lua_state = NULL;
254 if (filter->script_file) {
255 free(filter->script_file);
256 filter->script_file = NULL;
257 }
258}
259
260static int init_lua_filter(struct lua_filter *filter)
261{
262 if (filter->lua_state)
263 return 0;
264
265 if (!(filter->lua_state = luaL_newstate()))
266 return 1;
267
268 luaL_openlibs(filter->lua_state);
269
270 lua_pushcfunction(filter->lua_state, html_lua_filter);
271 lua_setglobal(filter->lua_state, "html");
272 lua_pushcfunction(filter->lua_state, html_txt_lua_filter);
273 lua_setglobal(filter->lua_state, "html_txt");
274 lua_pushcfunction(filter->lua_state, html_attr_lua_filter);
275 lua_setglobal(filter->lua_state, "html_attr");
276 lua_pushcfunction(filter->lua_state, html_url_path_lua_filter);
277 lua_setglobal(filter->lua_state, "html_url_path");
278 lua_pushcfunction(filter->lua_state, html_url_arg_lua_filter);
279 lua_setglobal(filter->lua_state, "html_url_arg");
280
281 if (luaL_dofile(filter->lua_state, filter->script_file)) {
282 error_lua_filter(filter);
283 lua_close(filter->lua_state);
284 filter->lua_state = NULL;
285 return 1;
286 }
287 return 0;
288}
289
290static int open_lua_filter(struct cgit_filter *base, va_list ap)
291{
292 struct lua_filter *filter = (struct lua_filter *) base;
293 int i;
294
295 if (init_lua_filter(filter))
296 return 1;
297
298 hook_write(base, write_lua_filter);
299
300 lua_getglobal(filter->lua_state, "filter_open");
301 for (i = 0; i < filter->base.argument_count; ++i)
302 lua_pushstring(filter->lua_state, va_arg(ap, char *));
303 if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) {
304 error_lua_filter(filter);
305 return 1;
306 }
307 return 0;
308}
309
310static int close_lua_filter(struct cgit_filter *base)
311{
312 struct lua_filter *filter = (struct lua_filter *) base;
313 int ret = 0;
314
315 lua_getglobal(filter->lua_state, "filter_close");
316 if (lua_pcall(filter->lua_state, 0, 0, 0)) {
317 error_lua_filter(filter);
318 ret = 1;
319 }
320 unhook_write();
321 return ret;
322}
323
324static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
325{
326 struct lua_filter *filter = (struct lua_filter *) base;
327 fprintf(f, "%slua:%s\n", prefix, filter->script_file);
328}
329
330
331static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
332{
333 struct lua_filter *filter;
334
335 filter = xmalloc(sizeof(*filter));
336 memset(filter, 0, sizeof(*filter));
337 filter->base.open = open_lua_filter;
338 filter->base.close = close_lua_filter;
339 filter->base.fprintf = fprintf_lua_filter;
340 filter->base.cleanup = cleanup_lua_filter;
341 filter->base.argument_count = argument_count;
342 filter->script_file = xstrdup(cmd);
343
344 return &filter->base;
345}
346
347#endif
348
349
167int cgit_open_filter(struct cgit_filter *filter, ...) 350int cgit_open_filter(struct cgit_filter *filter, ...)
168{ 351{
169 int result; 352 int result;
@@ -191,6 +374,9 @@ static const struct {
191 struct cgit_filter *(*ctor)(const char *cmd, int argument_count); 374 struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
192} filter_specs[] = { 375} filter_specs[] = {
193 { "exec", new_exec_filter }, 376 { "exec", new_exec_filter },
377#ifndef NO_LUA
378 { "lua", new_lua_filter },
379#endif
194}; 380};
195 381
196struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) 382struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)