💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › ed48781f2a396145b343f524763cc… captured on 2024-02-05 at 10:03:44. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <pthread.h>
8 #include "termbox.h"
9 #include "macro.h"
10 #include "config.h"
11 #include "client.h"
12 #include "gemini.h"
13 #include "page.h"
14 #include "request.h"
15 #include "parser.h"
16 #include "secure.h"
17 #include "tab.h"
18 #include "strlcpy.h"
19 #include "url.h"
20 #include "error.h"
21
22 struct request_thread {
23 pthread_t thread;
24 struct tab *tab;
25 struct request *request;
26 char url[MAX_URL];
27 };
28
29 struct tab *tab_new(void) {
30 struct tab *tab = calloc(1, sizeof(struct tab));
31 if (!tab) return NULL;
32 tab->mutex = malloc(sizeof(pthread_mutex_t));
33 if (!tab->mutex) {
34 free(tab);
35 return NULL;
36 }
37 pthread_mutex_init(tab->mutex, NULL);
38 return tab;
39 }
40
41 struct request *tab_request_new(struct tab *tab) {
42
43 struct request *new, *req;
44
45 new = calloc(1, sizeof(struct request));
46 if (!new) return NULL;
47 pthread_mutex_lock(tab->mutex);
48 new->next = tab->request;
49 for (req = new->next; req; req = req->next)
50 request_cancel(req);
51 tab->request = new;
52 pthread_mutex_unlock(tab->mutex);
53 return new;
54 }
55
56 void tab_clean_requests(struct tab *tab) {
57
58 struct request *request, *prev, *next;
59 int found;
60
61 if (!tab) return;
62
63 pthread_mutex_lock(tab->mutex);
64 found = 0;
65 for (request = tab->request; request; request = request->next) {
66 if (request->state == STATE_COMPLETED) {
67 if (found++ > config.maximumCachedPages)
68 request->state = STATE_ENDED;
69 }
70 }
71 prev = NULL;
72 for (request = tab->request; request; request = next) {
73 next = request->next;
74 if (request != tab->request &&
75 request->state == STATE_FAILED)
76 request->state = STATE_ENDED;
77 if (request->state == STATE_ENDED) {
78 if (prev)
79 prev->next = request->next;
80 if (request == tab->request) {
81 if (prev) tab->request = prev;
82 else tab->request = request->next;
83 }
84 request_free(request);
85 } else prev = request;
86 }
87 pthread_mutex_unlock(tab->mutex);
88 }
89
90 void* tab_request_thread(void *ptr) {
91
92 struct request_thread *args = ptr;
93 struct tab *tab = args->tab;
94 struct request *req = args->request; /* copy next to the ref */
95 struct request request = {0};
96 struct secure *ctx;
97 char destination[MAX_URL];
98 int failure;
99 int redirect;
100 int iterations;
101 int confirm;
102
103 iterations = 0;
104 restart:
105 if (iterations > config.maximumRedirects) {
106 request.error = ERROR_TOO_MANY_REDIRECT;
107 error_string(request.error, V(tab->error));
108 tab->failure = 1;
109 request.next = req->next;
110 request.status = -1;
111 request.state = STATE_FAILED;
112 *req = request;
113 tb_refresh(); /* refresh screen */
114 return 0;
115 }
116 redirect = 0;
117 ctx = secure_new();
118 failure = request_process(&request, ctx, args->url);
119 pthread_mutex_lock(tab->mutex);
120 confirm = 1;
121 if (req->state == STATE_ABANDONED) {
122 request_free_ref(request);
123 request_free(req);
124 confirm = 0;
125 } else if (req->state == STATE_CANCELED) {
126 request_free_ref(request);
127 confirm = 0;
128 } else if (request.state == STATE_CANCELED) {
129 confirm = 0;
130 req->state = STATE_CANCELED;
131 } else if (failure) {
132 {
133 char error[1024];
134 error_string(request.error, V(error));
135 snprintf(V(tab->error), "%s: %s", error, request.url);
136 }
137 tab->failure = 1;
138 } else if (gemini_isredirect(request.status)) {
139 redirect = 1;
140 STRLCPY(destination, request.meta);
141 confirm = 0;
142 } else if (request.status == -1) {
143 request.error = ERROR_INVALID_DATA;
144 error_string(request.error, V(tab->error));
145 tab->failure = 1;
146 request.state = STATE_FAILED;
147 } else if (gemini_iserror(request.status)) {
148 {
149 char status[1024] = {0};
150 gemini_status_string(request.status, V(status));
151 snprintf(V(tab->error), "%s (%d : %s)", request.meta,
152 request.status, status);
153 }
154 tab->failure = 1;
155 request.state = STATE_FAILED;
156 }
157 if (confirm) {
158 request.next = req->next;
159 *req = request;
160 tb_refresh(); /* refresh screen */
161 }
162 pthread_mutex_unlock(tab->mutex);
163
164 secure_free(ctx);
165 if (redirect) {
166 int protocol = protocol_from_url(destination);
167 if (protocol != PROTOCOL_NONE && protocol != PROTOCOL_GEMINI) {
168 request.error = ERROR_UNSUPPORTED_PROTOCOL;
169 error_string(request.error, V(tab->error));
170 tab->failure = 1;
171 request.state = STATE_FAILED;
172 } else {
173 request_follow(&request, destination, V(args->url));
174 iterations++;
175 goto restart;
176 }
177 }
178 free(args);
179 tab_clean_requests(tab);
180 return NULL;
181 }
182
183 int tab_request(struct tab* tab, const char *url) {
184
185 struct request_thread *args;
186 pthread_attr_t tattr = {0};
187
188 /* clean up forward history */
189 pthread_mutex_lock(tab->mutex);
190 if (tab->view) {
191 struct request *req, *next;
192 for (req = tab->request; req; req = next) {
193 if (req == tab->view) break;
194 next = req->next;
195 if (req->state == STATE_ONGOING ||
196 req->state == STATE_CANCELED) {
197 req->state = STATE_ABANDONED;
198 } else request_free(req);
199 }
200 tab->request = tab->view;
201 tab->view = NULL;
202 }
203 pthread_mutex_unlock(tab->mutex);
204
205 args = malloc(sizeof(*args));
206 if (!args) return ERROR_MEMORY_FAILURE;
207 STRLCPY(args->url, url);
208 args->tab = tab;
209 args->request = tab_request_new(tab);
210 if (!args->request) {
211 free(args);
212 return ERROR_MEMORY_FAILURE;
213 }
214 if (pthread_attr_init(&tattr)) return ERROR_PTHREAD;
215 if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED))
216 return ERROR_PTHREAD;
217 pthread_create(&args->thread, &tattr, tab_request_thread, args);
218 return 0;
219 }
220
221 int tab_follow(struct tab* tab, const char *link) {
222
223 struct request *req;
224 char url[MAX_URL], buf[MAX_URL];
225 int ret;
226
227 if (!tab) return 0;
228 format_link(link, MAX_URL, V(buf));
229 if (!(req = tab_completed(tab))) return 0;
230 if ((ret = request_follow(tab->request, buf, V(url)))) return ret;
231 tab_request(tab, url);
232
233 return 0;
234 }
235
236 int tab_scroll(struct tab *tab, int scroll, struct rect rect) {
237 struct request *req = tab_completed(tab);
238 if (!req) return -1;
239 return request_scroll(req, scroll, rect);
240 }
241
242 struct request *tab_input(struct tab *tab) {
243 struct request *req;
244 if (!tab) return NULL;
245 for (req = tab->request; req; req = req->next) {
246 if (req->state != STATE_COMPLETED) continue;
247 if (req->status == GMI_INPUT || req->status == GMI_SECRET)
248 return req;
249 }
250 return NULL;
251 }
252
253 struct request *tab_completed(struct tab *tab) {
254 struct request *req;
255 if (!tab) return NULL;
256 if (tab->view) return tab->view;
257 for (req = tab->request; req; req = req->next) {
258 if (req->state == STATE_COMPLETED &&
259 req->status == GMI_SUCCESS) {
260 return req;
261 }
262 }
263 return NULL;
264 }
265
266 void tab_free(struct tab *tab) {
267 struct request *req = tab->request;
268 while (req) {
269 struct request *next = req->next;
270 request_free(req);
271 req = next;
272 }
273 tab->request = NULL;
274 pthread_mutex_destroy(tab->mutex);
275 free(tab->mutex);
276 free(tab);
277 }
278