summaryrefslogtreecommitdiff
path: root/src/gmrequest.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gmrequest.c')
-rw-r--r--src/gmrequest.c187
1 files changed, 95 insertions, 92 deletions
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 7b110c03..692267a5 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -9,7 +9,44 @@
9 9
10#include <SDL_timer.h> 10#include <SDL_timer.h>
11 11
12static const int BODY_TIMEOUT = 3000; /* ms */ 12void init_GmResponse(iGmResponse *d) {
13 d->statusCode = none_GmStatusCode;
14 init_String(&d->meta);
15 init_Block(&d->body, 0);
16 d->certFlags = 0;
17 iZap(d->certValidUntil);
18}
19
20void initCopy_GmResponse(iGmResponse *d, const iGmResponse *other) {
21 d->statusCode = other->statusCode;
22 initCopy_String(&d->meta, &other->meta);
23 initCopy_Block(&d->body, &other->body);
24 d->certFlags = other->certFlags;
25 d->certValidUntil = other->certValidUntil;
26}
27
28void deinit_GmResponse(iGmResponse *d) {
29 deinit_Block(&d->body);
30 deinit_String(&d->meta);
31}
32
33void clear_GmResponse(iGmResponse *d) {
34 d->statusCode = none_GmStatusCode;
35 clear_String(&d->meta);
36 clear_Block(&d->body);
37 d->certFlags = 0;
38 iZap(d->certValidUntil);
39}
40
41iGmResponse *copy_GmResponse(const iGmResponse *d) {
42 iGmResponse *copied = iMalloc(GmResponse);
43 initCopy_GmResponse(copied, d);
44 return copied;
45}
46
47/*----------------------------------------------------------------------------------------------*/
48
49static const int bodyTimeout_GmRequest_ = 3000; /* ms */
13 50
14enum iGmRequestState { 51enum iGmRequestState {
15 initialized_GmRequestState, 52 initialized_GmRequestState,
@@ -22,12 +59,9 @@ struct Impl_GmRequest {
22 iObject object; 59 iObject object;
23 iMutex mutex; 60 iMutex mutex;
24 enum iGmRequestState state; 61 enum iGmRequestState state;
25 int certFlags;
26 iString url; 62 iString url;
27 iTlsRequest *req; 63 iTlsRequest *req;
28 enum iGmStatusCode code; 64 iGmResponse resp;
29 iString header;
30 iBlock body; /* rest of the received data */
31 uint32_t timeoutId; /* in case server doesn't close the connection */ 65 uint32_t timeoutId; /* in case server doesn't close the connection */
32 iAudience *updated; 66 iAudience *updated;
33 iAudience *finished; 67 iAudience *finished;
@@ -40,12 +74,9 @@ iDefineAudienceGetter(GmRequest, finished)
40void init_GmRequest(iGmRequest *d) { 74void init_GmRequest(iGmRequest *d) {
41 init_Mutex(&d->mutex); 75 init_Mutex(&d->mutex);
42 d->state = initialized_GmRequestState; 76 d->state = initialized_GmRequestState;
43 d->certFlags = 0; 77 init_GmResponse(&d->resp);
44 init_String(&d->url); 78 init_String(&d->url);
45 d->req = NULL; 79 d->req = NULL;
46 d->code = none_GmStatusCode;
47 init_String(&d->header);
48 init_Block(&d->body, 0);
49 d->timeoutId = 0; 80 d->timeoutId = 0;
50 d->updated = NULL; 81 d->updated = NULL;
51 d->finished = NULL; 82 d->finished = NULL;
@@ -72,8 +103,7 @@ void deinit_GmRequest(iGmRequest *d) {
72 d->req = NULL; 103 d->req = NULL;
73 delete_Audience(d->finished); 104 delete_Audience(d->finished);
74 delete_Audience(d->updated); 105 delete_Audience(d->updated);
75 deinit_Block(&d->body); 106 deinit_GmResponse(&d->resp);
76 deinit_String(&d->header);
77 deinit_String(&d->url); 107 deinit_String(&d->url);
78 deinit_Mutex(&d->mutex); 108 deinit_Mutex(&d->mutex);
79} 109}
@@ -95,25 +125,26 @@ static void restartTimeout_GmRequest_(iGmRequest *d) {
95 if (d->timeoutId) { 125 if (d->timeoutId) {
96 SDL_RemoveTimer(d->timeoutId); 126 SDL_RemoveTimer(d->timeoutId);
97 } 127 }
98 d->timeoutId = SDL_AddTimer(BODY_TIMEOUT, timedOutWhileReceivingBody_GmRequest_, d); 128 d->timeoutId = SDL_AddTimer(bodyTimeout_GmRequest_, timedOutWhileReceivingBody_GmRequest_, d);
99} 129}
100 130
101static void checkServerCertificate_GmRequest_(iGmRequest *d) { 131static void checkServerCertificate_GmRequest_(iGmRequest *d) {
102 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); 132 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req);
103 d->certFlags = 0; 133 d->resp.certFlags = 0;
104 if (cert) { 134 if (cert) {
105 iGmCerts * certDb = certs_App(); 135 iGmCerts * certDb = certs_App();
106 const iRangecc domain = urlHost_String(&d->url); 136 const iRangecc domain = urlHost_String(&d->url);
107 d->certFlags |= available_GmRequestCertFlag; 137 d->resp.certFlags |= available_GmCertFlag;
108 if (!isExpired_TlsCertificate(cert)) { 138 if (!isExpired_TlsCertificate(cert)) {
109 d->certFlags |= timeVerified_GmRequestCertFlag; 139 d->resp.certFlags |= timeVerified_GmCertFlag;
110 } 140 }
111 if (verifyDomain_TlsCertificate(cert, domain)) { 141 if (verifyDomain_TlsCertificate(cert, domain)) {
112 d->certFlags |= domainVerified_GmRequestCertFlag; 142 d->resp.certFlags |= domainVerified_GmCertFlag;
113 } 143 }
114 if (checkTrust_GmCerts(certDb, domain, cert)) { 144 if (checkTrust_GmCerts(certDb, domain, cert)) {
115 d->certFlags |= trusted_GmRequestCertFlag; 145 d->resp.certFlags |= trusted_GmCertFlag;
116 } 146 }
147 validUntil_TlsCertificate(serverCertificate_TlsRequest(d->req), &d->resp.certValidUntil);
117 } 148 }
118} 149}
119 150
@@ -126,35 +157,35 @@ static void readIncoming_GmRequest_(iAnyObject *obj) {
126 iBlock *data = readAll_TlsRequest(d->req); 157 iBlock *data = readAll_TlsRequest(d->req);
127 fflush(stdout); 158 fflush(stdout);
128 if (d->state == receivingHeader_GmRequestState) { 159 if (d->state == receivingHeader_GmRequestState) {
129 appendCStrN_String(&d->header, constData_Block(data), size_Block(data)); 160 appendCStrN_String(&d->resp.meta, constData_Block(data), size_Block(data));
130 /* Check if the header line is complete. */ 161 /* Check if the header line is complete. */
131 size_t endPos = indexOfCStr_String(&d->header, "\r\n"); 162 size_t endPos = indexOfCStr_String(&d->resp.meta, "\r\n");
132 if (endPos != iInvalidPos) { 163 if (endPos != iInvalidPos) {
133 /* Move remainder to the body. */ 164 /* Move remainder to the body. */
134 setData_Block(&d->body, 165 setData_Block(&d->resp.body,
135 constBegin_String(&d->header) + endPos + 2, 166 constBegin_String(&d->resp.meta) + endPos + 2,
136 size_String(&d->header) - endPos - 2); 167 size_String(&d->resp.meta) - endPos - 2);
137 remove_Block(&d->header.chars, endPos, iInvalidSize); 168 remove_Block(&d->resp.meta.chars, endPos, iInvalidSize);
138 /* parse and remove the code */ 169 /* parse and remove the code */
139 if (size_String(&d->header) < 3) { 170 if (size_String(&d->resp.meta) < 3) {
140 clear_String(&d->header); 171 clear_String(&d->resp.meta);
141 d->code = invalidHeader_GmStatusCode; 172 d->resp.statusCode = invalidHeader_GmStatusCode;
142 d->state = finished_GmRequestState; 173 d->state = finished_GmRequestState;
143 notifyDone = iTrue; 174 notifyDone = iTrue;
144 } 175 }
145 const int code = toInt_String(&d->header); 176 const int code = toInt_String(&d->resp.meta);
146 if (code == 0 || cstr_String(&d->header)[2] != ' ') { 177 if (code == 0 || cstr_String(&d->resp.meta)[2] != ' ') {
147 clear_String(&d->header); 178 clear_String(&d->resp.meta);
148 d->code = invalidHeader_GmStatusCode; 179 d->resp.statusCode = invalidHeader_GmStatusCode;
149 d->state = finished_GmRequestState; 180 d->state = finished_GmRequestState;
150 notifyDone = iTrue; 181 notifyDone = iTrue;
151 } 182 }
152 remove_Block(&d->header.chars, 0, 3); /* just the meta */ 183 remove_Block(&d->resp.meta.chars, 0, 3); /* just the meta */
153 if (code == success_GmStatusCode && isEmpty_String(&d->header)) { 184 if (code == success_GmStatusCode && isEmpty_String(&d->resp.meta)) {
154 setCStr_String(&d->header, "text/gemini; charset=utf-8"); /* default */ 185 setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); /* default */
155 } 186 }
156 d->code = code; 187 d->resp.statusCode = code;
157 d->state = receivingBody_GmRequestState; 188 d->state = receivingBody_GmRequestState;
158 checkServerCertificate_GmRequest_(d); 189 checkServerCertificate_GmRequest_(d);
159 notifyUpdate = iTrue; 190 notifyUpdate = iTrue;
160 /* Start a timeout for the remainder of the response, in case the connection 191 /* Start a timeout for the remainder of the response, in case the connection
@@ -163,7 +194,7 @@ static void readIncoming_GmRequest_(iAnyObject *obj) {
163 } 194 }
164 } 195 }
165 else if (d->state == receivingBody_GmRequestState) { 196 else if (d->state == receivingBody_GmRequestState) {
166 append_Block(&d->body, data); 197 append_Block(&d->resp.body, data);
167 restartTimeout_GmRequest_(d); 198 restartTimeout_GmRequest_(d);
168 notifyUpdate = iTrue; 199 notifyUpdate = iTrue;
169 } 200 }
@@ -189,26 +220,6 @@ static void requestFinished_GmRequest_(iAnyObject *obj) {
189 d->timeoutId = 0; 220 d->timeoutId = 0;
190 d->state = finished_GmRequestState; 221 d->state = finished_GmRequestState;
191 checkServerCertificate_GmRequest_(d); 222 checkServerCertificate_GmRequest_(d);
192#if 0
193 printf("Server certificate:\n%s\n", cstrLocal_String(pem_TlsCertificate(cert)));
194 iBlock *sha = fingerprint_TlsCertificate(cert);
195 printf("Fingerprint: %s\n",
196 cstr_String(collect_String(
197 hexEncode_Block(collect_Block(fingerprint_TlsCertificate(cert))))));
198 delete_Block(sha);
199 iDate expiry;
200 validUntil_TlsCertificate(cert, &expiry);
201 printf("Valid until %04d-%02d-%02d\n", expiry.year, expiry.month, expiry.day);
202 printf("Has expired: %s\n", isExpired_TlsCertificate(cert) ? "yes" : "no");
203 //printf("Subject: %s\n", cstrLocal_String(subject_TlsCertificate(serverCertificate_TlsRequest(d->req))));
204 /* Verify. */ {
205 iUrl parts;
206 init_Url(&parts, &d->url);
207 printf("Domain name is %s\n",
208 verifyDomain_TlsCertificate(cert, parts.host) ? "valid" : "not valid");
209 }
210 fflush(stdout);
211#endif
212 unlock_Mutex(&d->mutex); 223 unlock_Mutex(&d->mutex);
213 iNotifyAudience(d, finished, GmRequestFinished); 224 iNotifyAudience(d, finished, GmRequestFinished);
214} 225}
@@ -218,10 +229,7 @@ void submit_GmRequest(iGmRequest *d) {
218 if (d->state != initialized_GmRequestState) { 229 if (d->state != initialized_GmRequestState) {
219 return; 230 return;
220 } 231 }
221 d->code = none_GmStatusCode; 232 clear_GmResponse(&d->resp);
222 clear_String(&d->header);
223 clear_Block(&d->body);
224 d->certFlags = 0;
225 iUrl url; 233 iUrl url;
226 init_Url(&url, &d->url); 234 init_Url(&url, &d->url);
227 if (equalCase_Rangecc(&url.protocol, "file")) { 235 if (equalCase_Rangecc(&url.protocol, "file")) {
@@ -230,50 +238,47 @@ void submit_GmRequest(iGmRequest *d) {
230 if (open_File(f, readOnly_FileMode)) { 238 if (open_File(f, readOnly_FileMode)) {
231 /* TODO: Check supported file types: images, audio */ 239 /* TODO: Check supported file types: images, audio */
232 /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ 240 /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */
233 d->code = success_GmStatusCode; 241 d->resp.statusCode = success_GmStatusCode;
234 d->certFlags = 0;
235 if (endsWithCase_String(path, ".gmi")) { 242 if (endsWithCase_String(path, ".gmi")) {
236 setCStr_String(&d->header, "text/gemini; charset=utf-8"); 243 setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8");
237 } 244 }
238 else if (endsWithCase_String(path, ".txt")) { 245 else if (endsWithCase_String(path, ".txt")) {
239 setCStr_String(&d->header, "text/plain"); 246 setCStr_String(&d->resp.meta, "text/plain");
240 } 247 }
241 else if (endsWithCase_String(path, ".png")) { 248 else if (endsWithCase_String(path, ".png")) {
242 setCStr_String(&d->header, "image/png"); 249 setCStr_String(&d->resp.meta, "image/png");
243 } 250 }
244 else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { 251 else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) {
245 setCStr_String(&d->header, "image/jpeg"); 252 setCStr_String(&d->resp.meta, "image/jpeg");
246 } 253 }
247 else if (endsWithCase_String(path, ".gif")) { 254 else if (endsWithCase_String(path, ".gif")) {
248 setCStr_String(&d->header, "image/gif"); 255 setCStr_String(&d->resp.meta, "image/gif");
249 } 256 }
250 else { 257 else {
251 setCStr_String(&d->header, "application/octet-stream"); 258 setCStr_String(&d->resp.meta, "application/octet-stream");
252 } 259 }
253 set_Block(&d->body, collect_Block(readAll_File(f))); 260 set_Block(&d->resp.body, collect_Block(readAll_File(f)));
254 d->state = receivingBody_GmRequestState; 261 d->state = receivingBody_GmRequestState;
255 iNotifyAudience(d, updated, GmRequestUpdated); 262 iNotifyAudience(d, updated, GmRequestUpdated);
256 } 263 }
257 else { 264 else {
258 d->code = failedToOpenFile_GmStatusCode; 265 d->resp.statusCode = failedToOpenFile_GmStatusCode;
259 setCStr_String(&d->header, cstr_String(path)); 266 setCStr_String(&d->resp.meta, cstr_String(path));
260 } 267 }
261 iRelease(f); 268 iRelease(f);
262 d->certFlags = 0;
263 d->state = finished_GmRequestState; 269 d->state = finished_GmRequestState;
264 iNotifyAudience(d, finished, GmRequestFinished); 270 iNotifyAudience(d, finished, GmRequestFinished);
265 return; 271 return;
266 } 272 }
267 else if (equalCase_Rangecc(&url.protocol, "data")) { 273 else if (equalCase_Rangecc(&url.protocol, "data")) {
268 d->code = success_GmStatusCode; 274 d->resp.statusCode = success_GmStatusCode;
269 d->certFlags = 0;
270 iString *src = collectNewCStr_String(url.protocol.start + 5); 275 iString *src = collectNewCStr_String(url.protocol.start + 5);
271 iRangecc header = { constBegin_String(src), constBegin_String(src) }; 276 iRangecc header = { constBegin_String(src), constBegin_String(src) };
272 while (header.end < constEnd_String(src) && *header.end != ',') { 277 while (header.end < constEnd_String(src) && *header.end != ',') {
273 header.end++; 278 header.end++;
274 } 279 }
275 iBool isBase64 = iFalse; 280 iBool isBase64 = iFalse;
276 setRange_String(&d->header, header); 281 setRange_String(&d->resp.meta, header);
277 /* Check what's in the header. */ { 282 /* Check what's in the header. */ {
278 iRangecc entry = iNullRange; 283 iRangecc entry = iNullRange;
279 while (nextSplit_Rangecc(&header, ";", &entry)) { 284 while (nextSplit_Rangecc(&header, ";", &entry)) {
@@ -289,7 +294,7 @@ void submit_GmRequest(iGmRequest *d) {
289 else { 294 else {
290 set_String(src, collect_String(urlDecode_String(src))); 295 set_String(src, collect_String(urlDecode_String(src)));
291 } 296 }
292 set_Block(&d->body, &src->chars); 297 set_Block(&d->resp.body, &src->chars);
293 d->state = receivingBody_GmRequestState; 298 d->state = receivingBody_GmRequestState;
294 iNotifyAudience(d, updated, GmRequestUpdated); 299 iNotifyAudience(d, updated, GmRequestUpdated);
295 d->state = finished_GmRequestState; 300 d->state = finished_GmRequestState;
@@ -317,19 +322,19 @@ iBool isFinished_GmRequest(const iGmRequest *d) {
317} 322}
318 323
319enum iGmStatusCode status_GmRequest(const iGmRequest *d) { 324enum iGmStatusCode status_GmRequest(const iGmRequest *d) {
320 return d->code; 325 return d->resp.statusCode;
321} 326}
322 327
323const iString *meta_GmRequest(const iGmRequest *d) { 328const iString *meta_GmRequest(const iGmRequest *d) {
324 if (d->state >= receivingBody_GmRequestState) { 329 if (d->state >= receivingBody_GmRequestState) {
325 return &d->header; 330 return &d->resp.meta;
326 } 331 }
327 return collectNew_String(); 332 return collectNew_String();
328} 333}
329 334
330const iBlock *body_GmRequest(const iGmRequest *d) { 335const iBlock *body_GmRequest(const iGmRequest *d) {
331 iBlock *body; 336 iBlock *body;
332 iGuardMutex(&d->mutex, body = collect_Block(copy_Block(&d->body))); 337 iGuardMutex(&d->mutex, body = collect_Block(copy_Block(&d->resp.body)));
333 return body; 338 return body;
334} 339}
335 340
@@ -337,19 +342,17 @@ const iString *url_GmRequest(const iGmRequest *d) {
337 return &d->url; 342 return &d->url;
338} 343}
339 344
345const iGmResponse *response_GmRequest(const iGmRequest *d) {
346 iAssert(d->state == finished_GmRequestState);
347 return &d->resp;
348}
349
340int certFlags_GmRequest(const iGmRequest *d) { 350int certFlags_GmRequest(const iGmRequest *d) {
341 return d->certFlags; 351 return d->resp.certFlags;
342} 352}
343 353
344iDate certExpirationDate_GmRequest(const iGmRequest *d) { 354iDate certExpirationDate_GmRequest(const iGmRequest *d) {
345 iDate expiry; 355 return d->resp.certValidUntil;
346 if (d->req) {
347 validUntil_TlsCertificate(serverCertificate_TlsRequest(d->req), &expiry);
348 }
349 else {
350 iZap(expiry);
351 }
352 return expiry;
353} 356}
354 357
355iDefineClass(GmRequest) 358iDefineClass(GmRequest)