comparison http.c @ 1:836a698946f8

pasterd: add basic routes
author David Demelier <markand@malikania.fr>
date Tue, 04 Feb 2020 16:44:43 +0100
parents
children 65607ae124b1
comparison
equal deleted inserted replaced
0:15a06aa20298 1:836a698946f8
1 /*
2 * http.c -- HTTP parsing and rendering
3 *
4 * Copyright (c) 2020 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 <sys/types.h>
20 #include <assert.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #include <kcgi.h>
29
30 #include "config.h"
31 #include "database.h"
32 #include "http.h"
33 #include "log.h"
34 #include "paste.h"
35 #include "util.h"
36
37 static void page_index(struct kreq *);
38 static void page_new(struct kreq *);
39 static void page_fork(struct kreq *);
40 static void page_paste(struct kreq *);
41 static void page_about(struct kreq *);
42 static void page_download(struct kreq *);
43
44 enum page {
45 PAGE_INDEX,
46 PAGE_NEW,
47 PAGE_FORK,
48 PAGE_PASTE,
49 PAGE_ABOUT,
50 PAGE_DOWNLOAD,
51 PAGE_LAST /* Not used. */
52 };
53
54 static const char *pages[] = {
55 [PAGE_INDEX] = "",
56 [PAGE_NEW] = "new",
57 [PAGE_FORK] = "fork",
58 [PAGE_PASTE] = "paste",
59 [PAGE_ABOUT] = "about",
60 [PAGE_DOWNLOAD] = "download",
61 };
62
63 static void (*handlers[])(struct kreq *req) = {
64 [PAGE_INDEX] = page_index,
65 [PAGE_NEW] = page_new,
66 [PAGE_FORK] = page_fork,
67 [PAGE_PASTE] = page_paste,
68 [PAGE_ABOUT] = page_about,
69 [PAGE_DOWNLOAD] = page_download
70 };
71
72 struct tmpl_index {
73 struct kreq *req;
74 struct paste pastes[10];
75 size_t count;
76 size_t current;
77 };
78
79 struct tmpl_paste {
80 struct kreq *req;
81 struct paste paste;
82 };
83
84 static const char *
85 template(const char *filename)
86 {
87 /* Build path to the template file. */
88 static char path[PATH_MAX];
89
90 snprintf(path, sizeof (path), "%s/%s", config.themedir, filename);
91
92 return path;
93 }
94
95 static int
96 tmpl_paste(size_t index, void *arg)
97 {
98 struct tmpl_paste *data = arg;
99 struct paste *paste = &data->paste;
100
101 switch (index) {
102 case 0:
103 khttp_puts(data->req, paste->uuid);
104 break;
105 case 1:
106 khttp_puts(data->req, paste->title);
107 break;
108 case 2:
109 khttp_puts(data->req, paste->author);
110 break;
111 case 3:
112 khttp_puts(data->req, paste->language);
113 break;
114 case 4:
115 khttp_puts(data->req, paste->code);
116 break;
117 case 5:
118 /* TODO: timestamp here. */
119 khttp_puts(data->req, "TODO");
120 break;
121 case 6:
122 khttp_puts(data->req, bprintf("%s", paste->visible ? "Yes" : "No"));
123 break;
124 case 7:
125 /* TODO: convert time left. */
126 khttp_puts(data->req, "TODO");
127 break;
128 default:
129 break;
130 }
131
132 return true;
133 }
134
135 static int
136 tmpl_index_pastes(size_t index, void *arg)
137 {
138 struct tmpl_index *data = arg;
139 struct paste *paste = &data->pastes[data->current];
140
141 switch (index) {
142 case 0:
143 khttp_puts(data->req, paste->uuid);
144 break;
145 case 1:
146 khttp_puts(data->req, paste->title);
147 break;
148 case 2:
149 khttp_puts(data->req, paste->author);
150 break;
151 case 3:
152 khttp_puts(data->req, paste->language);
153 break;
154 case 4:
155 khttp_puts(data->req, bprintf("%d", paste->duration));
156 break;
157 default:
158 break;
159 }
160
161 return true;
162 }
163
164 static int
165 tmpl_index(size_t index, void *arg)
166 {
167 /* No check, only one index. */
168 struct tmpl_index *data = arg;
169 const char *keywords[] = {
170 "uuid",
171 "name",
172 "author",
173 "language",
174 "expiration"
175 };
176 struct ktemplate kt = {
177 .key = keywords,
178 .keysz = 5,
179 .arg = data,
180 .cb = tmpl_index_pastes
181 };
182
183 for (size_t i = 0; i < data->count; ++i) {
184 khttp_template(data->req, &kt, template("index-paste.html"));
185 data->current++;
186 }
187
188 return true;
189 }
190
191 static void
192 header(struct kreq *req)
193 {
194 khttp_template(req, NULL, template("header.html"));
195 }
196
197 static void
198 footer(struct kreq *req)
199 {
200 khttp_template(req, NULL, template("footer.html"));
201 }
202
203 static void
204 page_index(struct kreq *req)
205 {
206 struct tmpl_index data = {
207 .req = req,
208 .count = 10
209 };
210
211 khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_HTML]);
212
213 if (!database_recents(data.pastes, &data.count)) {
214 khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_500]);
215 khttp_body(req);
216 khttp_template(req, NULL, template("500.html"));
217 } else {
218 const char *keywords[] = { "pastes" };
219 struct ktemplate kt = {
220 .key = keywords,
221 .keysz = 1,
222 .arg = &data,
223 .cb = tmpl_index
224 };
225
226 khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]);
227 khttp_body(req);
228 header(req);
229 khttp_template(req, &kt, template("index.html"));
230 footer(req);
231 }
232
233 khttp_free(req);
234 }
235
236 static void
237 page_new(struct kreq *req)
238 {
239 (void)req;
240 }
241
242 static void
243 page_fork(struct kreq *req)
244 {
245 (void)req;
246 }
247
248 static void
249 page_paste(struct kreq *req)
250 {
251 struct tmpl_paste data = {
252 .req = req
253 };
254
255 khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_TEXT_HTML]);
256
257 if (!database_get(&data.paste, req->path)) {
258 khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_404]);
259 khttp_body(req);
260 khttp_template(req, NULL, template("404.html"));
261 } else {
262 const char *keywords[] = {
263 "uuid",
264 "title",
265 "author",
266 "language",
267 "code",
268 "timestamp",
269 "visible",
270 "duration"
271 };
272 const struct ktemplate kt = {
273 .key = keywords,
274 .keysz = 8,
275 .cb = tmpl_paste,
276 .arg = &data
277 };
278
279 khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]);
280 khttp_body(req);
281 header(req);
282 khttp_template(req, &kt, template("paste.html"));
283 footer(req);
284 khttp_free(req);
285 }
286 }
287
288 static void
289 page_about(struct kreq *req)
290 {
291 (void)req;
292 }
293
294 static void
295 page_download(struct kreq *req)
296 {
297 (void)req;
298 }
299
300 static void
301 process(struct kreq *req)
302 {
303 assert(req);
304
305 handlers[req->page](req);
306 }
307
308 void
309 http_fcgi_run(void)
310 {
311 struct kreq req;
312 struct kfcgi *fcgi;
313
314 if (khttp_fcgi_init(&fcgi, NULL, 0, pages, PAGE_LAST, 0) != KCGI_OK)
315 return;
316
317 while (khttp_fcgi_parse(fcgi, &req) == KCGI_OK)
318 process(&req);
319
320 khttp_fcgi_free(fcgi);
321 }
322
323 void
324 http_cgi_run(void)
325 {
326 struct kreq req;
327
328 if (khttp_parse(&req, NULL, 0, pages, PAGE_LAST, 0) == KCGI_OK)
329 process(&req);
330 }