Squashed commit of the following:
[project/luci.git] / libs / sgi-webuci / src / luci.c
1 /*
2  * luci
3  * Copyright (C) 2008 John Crispin <blogic@openwrt.org>
4  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <string.h>
17 #include <stdio.h>
18 #include <boa-plugin.h>
19 #include <lauxlib.h>
20 #include <lualib.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23
24 #define LUAMAIN "luci.lua"
25
26 static lua_State *L = NULL;
27
28 extern int luci_parse_header (lua_State *L);
29
30 static int luci_init(struct httpd_plugin *p)
31 {
32         char *path = NULL;
33         int ret = 0;
34
35         L = luaL_newstate();
36         if (!L)
37                 goto error;
38
39         luaL_openlibs(L);
40
41         path = malloc(strlen(p->dir) + sizeof(LUAMAIN) + 2);
42         strcpy(path, p->dir);
43         strcat(path, "/" LUAMAIN);
44
45         ret = luaL_dofile(L, path);
46
47         lua_getfield(L, LUA_GLOBALSINDEX, "luci-plugin");
48         do {
49                 if (!lua_istable(L, -1)) {
50                         ret = 1;
51                         break;
52                 }
53
54                 lua_getfield(L, -1, "init");
55                 if (!lua_isfunction(L, -1))
56                         break;
57
58                 lua_pushstring(L, p->dir);
59                 ret = lua_pcall(L, 1, 0, 0);
60         } while (0);
61         free(path);
62
63         if (ret != 0)
64                 goto error;
65
66         return 1;
67
68 error:
69         fprintf(stderr, "Error: ");
70         if (L) {
71                 const char *s = lua_tostring(L, -1);
72                 if (!s)
73                         s = "unknown error";
74                 fprintf(stderr, "%s\n", s);
75                 lua_close(L);
76         } else {
77                 fprintf(stderr, "Out of memory!\n");
78         }
79         return 0;
80 }
81
82 static void pushvar(char *name, char *val)
83 {
84         if (!val)
85                 return;
86
87         lua_pushstring(L, val);
88         lua_setfield(L, -2, name);
89 }
90
91 static int luci_pcall(lua_State *L, char *func, int narg)
92 {
93         int ret;
94
95         ret = lua_pcall(L, narg, narg, 0);
96         if (ret) {
97                 const char *s = lua_tostring(L, -1);
98                 if (s)
99                         fprintf(stderr, "Error running %s: %s\n", func, s);
100                 return ret;
101         }
102         if (!narg)
103                 return ret;
104
105         ret = lua_isnumber(L, -1);
106         if (!ret)
107                 goto done;
108
109         ret = lua_tonumber(L, -1);
110
111 done:
112         lua_pop(L, 1);
113         return ret;
114 }
115
116 static int luci_prepare_req(struct httpd_plugin *p, struct http_context *ctx)
117 {
118         int ret;
119
120         lua_getglobal(L, "luci-plugin");
121         lua_getfield(L, -1, "prepare_req");
122
123         ret = lua_isfunction(L, -1);
124         if (!ret)
125                 goto done;
126
127         lua_pushstring(L, ctx->uri);
128
129         ret = luci_pcall(L, "prepare_req", 1);
130
131 done:
132         lua_pop(L, 1);
133         return ret;
134 }
135
136 static int luci_handle_req(struct httpd_plugin *p, struct http_context *ctx)
137 {
138         int ret;
139
140         lua_newtable(L); /* new table for the http context */
141
142         /* convert http_context data structure to lua table */
143 #define PUSH(x) pushvar(#x, ctx->x)
144         PUSH(request_method);
145         PUSH(server_addr);
146         PUSH(server_proto);
147         PUSH(query_string);
148         PUSH(remote_addr);
149         lua_pushinteger(L, ctx->remote_port);
150         lua_setfield(L, -2, "remote_port");
151         PUSH(content_type);
152         PUSH(content_length);
153         PUSH(http_accept);
154 #undef PUSH
155
156         if (!strncmp(ctx->uri, p->prefix, strlen(p->prefix)))
157                 pushvar("uri", ctx->uri + strlen(p->prefix));
158         else
159                 pushvar("uri", ctx->uri);
160
161
162         /* make sure the global 'luci' table is prepared */
163         lua_getglobal(L, "luci-plugin");
164         if (!lua_istable(L, -1))
165                 return 0;
166
167         lua_getfield(L, -1, "init_req");
168         if (!lua_isfunction(L, -1)) {
169                 /* ignore error */
170                 lua_pop(L, 1);
171         } else {
172                 lua_pushvalue(L, -3);
173                 luci_pcall(L, "init_req", 1);
174         }
175
176         /* storage space for cgi variables */
177         lua_newtable(L);
178         lua_pushvalue(L, -1); /* copy for setfield */
179         lua_setfield(L, -3, "vars");
180
181         lua_pushvalue(L, -3); /* the http context table */
182
183         /* 
184          * make luci_parse_header a closure
185          * argument 1: the luci.vars table
186          * argument 2: the http context table
187          */
188         lua_pushcclosure(L, luci_parse_header, 2);
189         ret = luci_pcall(L, "parse_header", 0);
190
191         lua_getfield(L, -1, "handle_req");
192         ret = lua_isfunction(L, -1);
193         if (!ret)
194                 goto done;
195
196         lua_pushvalue(L, -3);
197         ret = luci_pcall(L, "handle_req", 1);
198
199         /* pop the luci and http context tables */
200 done:
201         lua_pop(L, 2);
202         return ret;
203 }
204
205 static void luci_unload(struct httpd_plugin *p)
206 {
207         lua_close(L);
208 }
209
210 HTTPD_PLUGIN {
211         .prefix = "/luci/",
212         .init = luci_init,
213         .done = luci_unload,
214         .prepare_req = luci_prepare_req,
215         .handle_req = luci_handle_req,
216 };