💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › 16f2dc7c5ee432d68f999c2039abe… captured on 2023-12-28 at 15:45:42. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

Go Back

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() {

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