Blob


1 #include "telescope.h"
3 #include <sys/socket.h>
5 #include <err.h>
6 #include <errno.h>
7 #include <event.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
14 struct event imsgev;
15 struct tabshead tabshead;
17 static struct imsgbuf *ibuf;
19 static void handle_imsg_err(struct imsg*, size_t);
20 static void handle_imsg_check_cert(struct imsg*, size_t);
21 static void handle_imsg_got_code(struct imsg*, size_t);
22 static void handle_imsg_got_meta(struct imsg*, size_t);
23 static void handle_imsg_buf(struct imsg*, size_t);
24 static void handle_imsg_eof(struct imsg*, size_t);
26 static void load_page_from_str(struct tab*, const char*);
28 static imsg_handlerfn *handlers[] = {
29 [IMSG_ERR] = handle_imsg_err,
30 [IMSG_CHECK_CERT] = handle_imsg_check_cert,
31 [IMSG_GOT_CODE] = handle_imsg_got_code,
32 [IMSG_GOT_META] = handle_imsg_got_meta,
33 [IMSG_BUF] = handle_imsg_buf,
34 [IMSG_EOF] = handle_imsg_eof,
35 };
37 static void __attribute__((__noreturn__))
38 die(void)
39 {
40 abort(); /* TODO */
41 }
43 static struct tab *
44 tab_by_id(uint32_t id)
45 {
46 struct tab *t;
48 TAILQ_FOREACH(t, &tabshead, tabs) {
49 if (t->id == id)
50 return t;
51 }
53 die();
54 }
56 static void
57 handle_imsg_err(struct imsg *imsg, size_t datalen)
58 {
59 struct tab *tab;
60 char *page;
62 tab = tab_by_id(imsg->hdr.peerid);
64 page = imsg->data;
65 page[datalen-1] = '\0';
67 if (asprintf(&page, "# Error loading %s\n\n> %s\n",
68 tab->url, page) == -1)
69 die();
70 load_page_from_str(tab, page);
71 free(page);
72 }
74 static void
75 handle_imsg_check_cert(struct imsg *imsg, size_t datalen)
76 {
77 int tofu_res = 1;
79 imsg_compose(ibuf, IMSG_CERT_STATUS, imsg->hdr.peerid, 0, -1, &tofu_res, sizeof(tofu_res));
80 imsg_flush(ibuf);
81 }
83 static void
84 handle_imsg_got_code(struct imsg *imsg, size_t datalen)
85 {
86 const char *errpage;
87 struct tab *tab;
89 tab = tab_by_id(imsg->hdr.peerid);
91 if (sizeof(tab->code) != datalen)
92 die();
93 memcpy(&tab->code, imsg->data, sizeof(tab->code));
95 if (tab->code < 20) {
96 if (tab->code != 10 && tab->code != 11)
97 tab->code = 10;
98 } else if (tab->code < 30)
99 tab->code = 20;
100 else if (tab->code < 40)
101 tab->code = 30;
102 else if (tab->code < 50)
103 tab->code = 40;
104 else if (tab->code < 60)
105 tab->code = 50;
106 else
107 tab->code = 60;
109 if (tab->code != 30)
110 tab->redirect_count = 0;
113 static void
114 handle_imsg_got_meta(struct imsg *imsg, size_t datalen)
116 struct tab *tab;
118 tab = tab_by_id(imsg->hdr.peerid);
120 if (sizeof(tab->meta) <= datalen)
121 die();
122 memcpy(tab->meta, imsg->data, datalen);
124 if (tab->code != 30)
125 tab->redirect_count = 0;
127 if (tab->code == 20) {
128 /* TODO: parse the MIME type */
129 gemtext_initparser(&tab->page);
130 imsg_compose(ibuf, IMSG_PROCEED, tab->id, 0, -1, NULL, 0);
131 imsg_flush(ibuf);
132 return;
135 if (tab->code == 30) {
136 tab->redirect_count++;
138 /* TODO: make customizable? */
139 if (tab->redirect_count > 5) {
140 load_page_from_str(tab,
141 err_pages[TOO_MUCH_REDIRECTS]);
142 return;
145 load_url(tab, tab->meta);
146 return;
149 /* 4x, 5x or 6x */
150 load_page_from_str(tab, err_pages[tab->code]);
153 static void
154 handle_imsg_buf(struct imsg *imsg, size_t datalen)
156 struct tab *tab;
158 tab = tab_by_id(imsg->hdr.peerid);
160 if (!tab->page.parse(&tab->page, imsg->data, datalen))
161 die();
163 ui_on_tab_refresh(tab);
166 static void
167 handle_imsg_eof(struct imsg *imsg, size_t datalen)
169 struct tab *t;
171 t = tab_by_id(imsg->hdr.peerid);
172 if (!t->page.free(&t->page))
173 die();
175 ui_on_tab_refresh(t);
176 ui_on_tab_loaded(t);
179 static void
180 dispatch_imsg(int fd, short ev, void *d)
182 struct imsg imsg;
183 size_t datalen;
184 ssize_t n;
186 if ((n = imsg_read(ibuf)) == -1) {
187 if (errno == EAGAIN || errno == EWOULDBLOCK)
188 return;
189 die();
192 if (n == 0) {
193 fprintf(stderr, "other side is dead\n");
194 exit(0);
197 for (;;) {
198 if ((n = imsg_get(ibuf, &imsg)) == -1)
199 die();
200 if (n == 0)
201 return;
202 datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
203 handlers[imsg.hdr.type](&imsg, datalen);
204 imsg_free(&imsg);
208 static void
209 load_page_from_str(struct tab *tab, const char *page)
211 gemtext_initparser(&tab->page);
212 if (!tab->page.parse(&tab->page, page, strlen(page)))
213 die();
214 if (!tab->page.free(&tab->page))
215 die();
216 ui_on_tab_refresh(tab);
217 ui_on_tab_loaded(tab);
220 void
221 load_url(struct tab *tab, const char *url)
223 /* TODO: parsing the url here is a bit ugly. maybe store it
224 * in the struct tab? */
225 struct url u;
226 const char *err;
227 char *page;
229 if (!strcmp(url, "about:new")) {
230 strlcpy(tab->url, url, sizeof(tab->url));
231 load_page_from_str(tab, about_new);
232 return;
235 if (has_prefix(tab->url, "about:")) {
236 strlcpy(tab->url, url, sizeof(tab->url));
237 } else {
238 if (!url_parse(tab->url, &u, &err))
239 goto err;
241 if (!url_resolve_from(&u, url, &err))
242 goto err;
244 /* TODO: this is nowhere good enough */
245 strlcpy(tab->url, u.scheme, sizeof(tab->url));
246 strlcat(tab->url, "://", sizeof(tab->url));
247 strlcat(tab->url, u.host, sizeof(tab->url));
248 strlcat(tab->url, "/", sizeof(tab->url));
249 strlcat(tab->url, u.path, sizeof(tab->url));
252 imsg_compose(ibuf, IMSG_GET, tab->id, 0, -1, url, strlen(url)+1);
253 imsg_flush(ibuf);
254 return;
256 err:
257 if (asprintf(&page, "# error resolving %s from %s\n\n> %s\n",
258 url, tab->url, err) == -1)
259 die();
260 load_page_from_str(tab, page);
261 free(page);
264 int
265 main(void)
267 struct imsgbuf main_ibuf, network_ibuf;
268 int imsg_fds[2];
270 signal(SIGCHLD, SIG_IGN);
272 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1)
273 err(1, "socketpair");
275 switch (fork()) {
276 case -1:
277 err(1, "fork");
278 case 0:
279 /* child */
280 setproctitle("client");
281 close(imsg_fds[0]);
282 imsg_init(&network_ibuf, imsg_fds[1]);
283 exit(client_main(&network_ibuf));
286 close(imsg_fds[1]);
287 imsg_init(&main_ibuf, imsg_fds[0]);
288 ibuf = &main_ibuf;
290 TAILQ_INIT(&tabshead);
292 event_init();
294 event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST, dispatch_imsg, ibuf);
295 event_add(&imsgev, NULL);
297 ui_init();
299 event_dispatch();
301 imsg_compose(ibuf, IMSG_QUIT, 0, 0, -1, NULL, 0);
302 imsg_flush(ibuf);
304 ui_end();
306 return 0;