Cleanup
[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 lua_State *luci_context_init(struct httpd_plugin *p)
31 {
32         char *path = NULL;
33         lua_State *Lnew;
34         int ret = 0;
35
36         Lnew = luaL_newstate();
37         if (!Lnew)
38                 goto error;
39
40         luaL_openlibs(Lnew);
41
42         path = malloc(strlen(p->dir) + sizeof(LUAMAIN) + 2);
43         strcpy(path, p->dir);
44         strcat(path, "/" LUAMAIN);
45
46         ret = luaL_dofile(Lnew, path);
47
48         lua_getfield(Lnew, LUA_GLOBALSINDEX, "luci-plugin");
49         do {
50                 if (!lua_istable(Lnew, -1)) {
51                         ret = 1;
52                         break;
53                 }
54
55                 lua_getfield(Lnew, -1, "init");
56                 if (!lua_isfunction(Lnew, -1))
57                         break;
58
59                 lua_pushstring(Lnew, p->dir);
60                 ret = lua_pcall(Lnew, 1, 0, 0);
61         } while (0);
62         free(path);
63
64         if (ret != 0)
65                 goto error;
66
67         return Lnew;
68
69 error:
70         fprintf(stderr, "Error: ");
71         if (Lnew) {
72                 const char *s = lua_tostring(Lnew, -1);
73                 if (!s)
74                         s = "unknown error";
75                 fprintf(stderr, "%s\n", s);
76                 lua_close(Lnew);
77         } else {
78                 fprintf(stderr, "Out of memory!\n");
79         }
80         return NULL;
81 }
82
83 static int luci_init(struct httpd_plugin *p)
84 {
85         L = luci_context_init(p);
86         return (L != NULL);
87 }
88
89 static void pushvar(char *name, char *val)
90 {
91         if (!val)
92                 return;
93
94         lua_pushstring(L, val);
95         lua_setfield(L, -2, name);
96 }
97
98 static int luci_pcall(lua_State *L, char *func, int narg)
99 {
100         int ret;
101
102         ret = lua_pcall(L, narg, narg, 0);
103         if (ret) {
104                 const char *s = lua_tostring(L, -1);
105                 if (s)
106                         fprintf(stderr, "Error running %s: %s\n", func, s);
107                 return ret;
108         }
109         if (!narg)
110                 return ret;
111
112         ret = lua_isnumber(L, -1);
113         if (!ret)
114                 goto done;
115
116         ret = lua_tonumber(L, -1);
117
118 done:
119         lua_pop(L, 1);
120         return ret;
121 }
122
123 static int luci_prepare_req(struct httpd_plugin *p, struct http_context *ctx)
124 {
125         int ret;
126         bool reload = false;
127
128         lua_getglobal(L, "luci-plugin");
129         lua_getfield(L, -1, "reload");
130         if (lua_isboolean(L, -1))
131                 reload = lua_toboolean(L, -1);
132         lua_pop(L, 1);
133
134         if (reload) {
135                 lua_close(L);
136                 L = luci_context_init(p);
137                 lua_getglobal(L, "luci-plugin");
138         }
139
140         lua_getfield(L, -1, "prepare_req");
141
142         ret = lua_isfunction(L, -1);
143         if (!ret)
144                 goto done;
145
146         lua_pushstring(L, ctx->uri);
147
148         ret = luci_pcall(L, "prepare_req", 1);
149
150 done:
151         lua_pop(L, 1);
152         return ret;
153 }
154
155 static int luci_handle_req(struct httpd_plugin *p, struct http_context *ctx)
156 {
157         int ret;
158
159         lua_newtable(L); /* new table for the http context */
160
161         /* convert http_context data structure to lua table */
162 #define PUSH(x) pushvar(#x, ctx->x)
163         PUSH(cookie);
164         PUSH(request_method);
165         PUSH(server_addr);
166         PUSH(server_proto);
167         PUSH(query_string);
168         PUSH(remote_addr);
169         lua_pushinteger(L, ctx->remote_port);
170         lua_setfield(L, -2, "remote_port");
171         PUSH(content_type);
172         PUSH(content_length);
173         PUSH(http_accept);
174 #undef PUSH
175
176         if (!strncmp(ctx->uri, p->prefix, strlen(p->prefix)))
177                 pushvar("uri", ctx->uri + strlen(p->prefix));
178         else
179                 pushvar("uri", ctx->uri);
180
181
182         /* make sure the global 'luci' table is prepared */
183         lua_getglobal(L, "luci-plugin");
184         if (!lua_istable(L, -1))
185                 return 0;
186
187         lua_getfield(L, -1, "init_req");
188         if (!lua_isfunction(L, -1)) {
189                 /* ignore error */
190                 lua_pop(L, 1);
191         } else {
192                 lua_pushvalue(L, -3);
193                 luci_pcall(L, "init_req", 1);
194         }
195
196         /* storage space for cgi variables */
197         lua_newtable(L);
198         lua_pushvalue(L, -1); /* copy for setfield */
199         lua_setfield(L, -3, "vars");
200
201         lua_pushvalue(L, -3); /* the http context table */
202
203         /* 
204          * make luci_parse_header a closure
205          * argument 1: the luci.vars table
206          * argument 2: the http context table
207          */
208         lua_pushcclosure(L, luci_parse_header, 2);
209         ret = luci_pcall(L, "parse_header", 0);
210
211         lua_getfield(L, -1, "handle_req");
212         ret = lua_isfunction(L, -1);
213         if (!ret)
214                 goto done;
215
216         lua_pushvalue(L, -3);
217         ret = luci_pcall(L, "handle_req", 1);
218
219         /* pop the luci and http context tables */
220 done:
221         lua_pop(L, 2);
222         return ret;
223 }
224
225 static void luci_unload(struct httpd_plugin *p)
226 {
227         lua_close(L);
228 }
229
230 HTTPD_PLUGIN {
231         .prefix = "/luci/",
232         .init = luci_init,
233         .done = luci_unload,
234         .prepare_req = luci_prepare_req,
235         .handle_req = luci_handle_req,
236 };