diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 14 | ||||
-rw-r--r-- | src/audio/player.c | 2 | ||||
-rw-r--r-- | src/gmdocument.c | 2 | ||||
-rw-r--r-- | src/gmrequest.c | 233 | ||||
-rw-r--r-- | src/gmrequest.h | 5 | ||||
-rw-r--r-- | src/media.c | 10 | ||||
-rw-r--r-- | src/media.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 51 |
8 files changed, 176 insertions, 143 deletions
@@ -99,7 +99,7 @@ struct Impl_App { | |||
99 | uint32_t lastTickerTime; | 99 | uint32_t lastTickerTime; |
100 | uint32_t elapsedSinceLastTicker; | 100 | uint32_t elapsedSinceLastTicker; |
101 | iBool running; | 101 | iBool running; |
102 | iBool pendingRefresh; | 102 | iAtomicInt pendingRefresh; |
103 | int tabEnum; | 103 | int tabEnum; |
104 | iStringList *launchCommands; | 104 | iStringList *launchCommands; |
105 | iBool isFinishedLaunching; | 105 | iBool isFinishedLaunching; |
@@ -355,7 +355,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
355 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); | 355 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); |
356 | d->running = iFalse; | 356 | d->running = iFalse; |
357 | d->window = NULL; | 357 | d->window = NULL; |
358 | d->pendingRefresh = iFalse; | 358 | set_Atomic(&d->pendingRefresh, iFalse); |
359 | d->certs = new_GmCerts(dataDir_App_); | 359 | d->certs = new_GmCerts(dataDir_App_); |
360 | d->visited = new_Visited(); | 360 | d->visited = new_Visited(); |
361 | d->bookmarks = new_Bookmarks(); | 361 | d->bookmarks = new_Bookmarks(); |
@@ -484,7 +484,7 @@ const iString *debugInfo_App(void) { | |||
484 | } | 484 | } |
485 | 485 | ||
486 | iLocalDef iBool isWaitingAllowed_App_(const iApp *d) { | 486 | iLocalDef iBool isWaitingAllowed_App_(const iApp *d) { |
487 | return !d->pendingRefresh && isEmpty_SortedArray(&d->tickers); | 487 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
488 | } | 488 | } |
489 | 489 | ||
490 | void processEvents_App(enum iAppEventMode eventMode) { | 490 | void processEvents_App(enum iAppEventMode eventMode) { |
@@ -583,11 +583,11 @@ void refresh_App(void) { | |||
583 | iApp *d = &app_; | 583 | iApp *d = &app_; |
584 | destroyPending_Widget(); | 584 | destroyPending_Widget(); |
585 | draw_Window(d->window); | 585 | draw_Window(d->window); |
586 | d->pendingRefresh = iFalse; | 586 | set_Atomic(&d->pendingRefresh, iFalse); |
587 | } | 587 | } |
588 | 588 | ||
589 | iBool isRefreshPending_App(void) { | 589 | iBool isRefreshPending_App(void) { |
590 | return app_.pendingRefresh; | 590 | return value_Atomic(&app_.pendingRefresh); |
591 | } | 591 | } |
592 | 592 | ||
593 | uint32_t elapsedSinceLastTicker_App(void) { | 593 | uint32_t elapsedSinceLastTicker_App(void) { |
@@ -642,8 +642,8 @@ int run_App(int argc, char **argv) { | |||
642 | 642 | ||
643 | void postRefresh_App(void) { | 643 | void postRefresh_App(void) { |
644 | iApp *d = &app_; | 644 | iApp *d = &app_; |
645 | if (!d->pendingRefresh) { | 645 | const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue); |
646 | d->pendingRefresh = iTrue; | 646 | if (!wasPending) { |
647 | SDL_Event ev; | 647 | SDL_Event ev; |
648 | ev.user.type = SDL_USEREVENT; | 648 | ev.user.type = SDL_USEREVENT; |
649 | ev.user.code = refresh_UserEventCode; | 649 | ev.user.code = refresh_UserEventCode; |
diff --git a/src/audio/player.c b/src/audio/player.c index 77c23104..1c8538b4 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -667,7 +667,7 @@ void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock * | |||
667 | const size_t newSize = size_Block(data); | 667 | const size_t newSize = size_Block(data); |
668 | iAssert(newSize >= oldSize); | 668 | iAssert(newSize >= oldSize); |
669 | /* The old parts cannot have changed. */ | 669 | /* The old parts cannot have changed. */ |
670 | iAssert(memcmp(constData_Block(&input->data), constData_Block(data), oldSize) == 0); | 670 | // iAssert(memcmp(constData_Block(&input->data), constData_Block(data), oldSize) == 0); |
671 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); | 671 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); |
672 | input->isComplete = iFalse; | 672 | input->isComplete = iFalse; |
673 | break; | 673 | break; |
diff --git a/src/gmdocument.c b/src/gmdocument.c index c8fc9869..50c79459 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -535,7 +535,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
535 | } | 535 | } |
536 | /* Flag the end of line, too. */ | 536 | /* Flag the end of line, too. */ |
537 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; | 537 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; |
538 | /* Image content. */ | 538 | /* Image or audio content. */ |
539 | if (type == link_GmLineType) { | 539 | if (type == link_GmLineType) { |
540 | const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); | 540 | const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); |
541 | const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0; | 541 | const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0; |
diff --git a/src/gmrequest.c b/src/gmrequest.c index 15782e01..14f77405 100644 --- a/src/gmrequest.c +++ b/src/gmrequest.c | |||
@@ -123,13 +123,15 @@ enum iGmRequestState { | |||
123 | 123 | ||
124 | struct Impl_GmRequest { | 124 | struct Impl_GmRequest { |
125 | iObject object; | 125 | iObject object; |
126 | iMutex mutex; | 126 | iMutex * mtx; |
127 | iGmCerts * certs; /* not owned */ | 127 | iGmCerts * certs; /* not owned */ |
128 | enum iGmRequestState state; | 128 | enum iGmRequestState state; |
129 | iString url; | 129 | iString url; |
130 | iTlsRequest * req; | 130 | iTlsRequest * req; |
131 | iGopher gopher; | 131 | iGopher gopher; |
132 | iGmResponse resp; | 132 | iGmResponse * resp; |
133 | iBool respLocked; | ||
134 | iAtomicInt allowUpdate; | ||
133 | iAudience * updated; | 135 | iAudience * updated; |
134 | iAudience * finished; | 136 | iAudience * finished; |
135 | }; | 137 | }; |
@@ -140,85 +142,77 @@ iDefineAudienceGetter(GmRequest, finished) | |||
140 | 142 | ||
141 | static void checkServerCertificate_GmRequest_(iGmRequest *d) { | 143 | static void checkServerCertificate_GmRequest_(iGmRequest *d) { |
142 | const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); | 144 | const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); |
143 | d->resp.certFlags = 0; | 145 | iGmResponse *resp = d->resp; |
146 | resp->certFlags = 0; | ||
144 | if (cert) { | 147 | if (cert) { |
145 | const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req))); | 148 | const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req))); |
146 | d->resp.certFlags |= available_GmCertFlag; | 149 | resp->certFlags |= available_GmCertFlag; |
147 | set_Block(&d->resp.certFingerprint, collect_Block(fingerprint_TlsCertificate(cert))); | 150 | set_Block(&resp->certFingerprint, collect_Block(fingerprint_TlsCertificate(cert))); |
148 | d->resp.certFlags |= haveFingerprint_GmCertFlag; | 151 | resp->certFlags |= haveFingerprint_GmCertFlag; |
149 | if (!isExpired_TlsCertificate(cert)) { | 152 | if (!isExpired_TlsCertificate(cert)) { |
150 | d->resp.certFlags |= timeVerified_GmCertFlag; | 153 | resp->certFlags |= timeVerified_GmCertFlag; |
151 | } | 154 | } |
152 | /* TODO: Check for IP too (see below), because it may be specified in the SAN. */ | ||
153 | #if 0 | ||
154 | iString *ip = toStringFlags_Address(address_TlsRequest(d->req), noPort_SocketStringFlag, 0); | ||
155 | if (verifyIp_TlsCertificate(cert, ip)) { | ||
156 | printf("[GmRequest] IP address %s matches!\n", cstr_String(ip)); | ||
157 | } | ||
158 | else { | ||
159 | printf("[GmRequest] IP address %s not matched\n", cstr_String(ip)); | ||
160 | } | ||
161 | delete_String(ip); | ||
162 | #endif | ||
163 | if (verifyDomain_TlsCertificate(cert, domain)) { | 155 | if (verifyDomain_TlsCertificate(cert, domain)) { |
164 | d->resp.certFlags |= domainVerified_GmCertFlag; | 156 | resp->certFlags |= domainVerified_GmCertFlag; |
165 | } | 157 | } |
166 | if (checkTrust_GmCerts(d->certs, domain, cert)) { | 158 | if (checkTrust_GmCerts(d->certs, domain, cert)) { |
167 | d->resp.certFlags |= trusted_GmCertFlag; | 159 | resp->certFlags |= trusted_GmCertFlag; |
168 | } | 160 | } |
169 | validUntil_TlsCertificate(cert, &d->resp.certValidUntil); | 161 | validUntil_TlsCertificate(cert, &resp->certValidUntil); |
170 | set_String(&d->resp.certSubject, collect_String(subject_TlsCertificate(cert))); | 162 | set_String(&resp->certSubject, collect_String(subject_TlsCertificate(cert))); |
171 | } | 163 | } |
172 | } | 164 | } |
173 | 165 | ||
174 | static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { | 166 | static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { |
175 | iBool notifyUpdate = iFalse; | 167 | iBool notifyUpdate = iFalse; |
176 | iBool notifyDone = iFalse; | 168 | iBool notifyDone = iFalse; |
177 | lock_Mutex(&d->mutex); | 169 | lock_Mutex(d->mtx); |
170 | iGmResponse *resp =d->resp; | ||
178 | iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ | 171 | iAssert(d->state != finished_GmRequestState); /* notifications out of order? */ |
179 | iBlock *data = readAll_TlsRequest(req); | 172 | iBlock *data = readAll_TlsRequest(req); |
180 | if (d->state == receivingHeader_GmRequestState) { | 173 | if (d->state == receivingHeader_GmRequestState) { |
181 | appendCStrN_String(&d->resp.meta, constData_Block(data), size_Block(data)); | 174 | appendCStrN_String(&resp->meta, constData_Block(data), size_Block(data)); |
182 | /* Check if the header line is complete. */ | 175 | /* Check if the header line is complete. */ |
183 | size_t endPos = indexOfCStr_String(&d->resp.meta, "\r\n"); | 176 | size_t endPos = indexOfCStr_String(&resp->meta, "\r\n"); |
184 | if (endPos != iInvalidPos) { | 177 | if (endPos != iInvalidPos) { |
185 | /* Move remainder to the body. */ | 178 | /* Move remainder to the body. */ |
186 | setData_Block(&d->resp.body, | 179 | setData_Block(&resp->body, |
187 | constBegin_String(&d->resp.meta) + endPos + 2, | 180 | constBegin_String(&resp->meta) + endPos + 2, |
188 | size_String(&d->resp.meta) - endPos - 2); | 181 | size_String(&resp->meta) - endPos - 2); |
189 | remove_Block(&d->resp.meta.chars, endPos, iInvalidSize); | 182 | remove_Block(&resp->meta.chars, endPos, iInvalidSize); |
190 | /* parse and remove the code */ | 183 | /* parse and remove the code */ |
191 | if (size_String(&d->resp.meta) < 3) { | 184 | if (size_String(&resp->meta) < 3) { |
192 | clear_String(&d->resp.meta); | 185 | clear_String(&resp->meta); |
193 | d->resp.statusCode = invalidHeader_GmStatusCode; | 186 | resp->statusCode = invalidHeader_GmStatusCode; |
194 | d->state = finished_GmRequestState; | 187 | d->state = finished_GmRequestState; |
195 | notifyDone = iTrue; | 188 | notifyDone = iTrue; |
196 | } | 189 | } |
197 | const int code = toInt_String(&d->resp.meta); | 190 | const int code = toInt_String(&resp->meta); |
198 | if (code == 0) { | 191 | if (code == 0) { |
199 | clear_String(&d->resp.meta); | 192 | clear_String(&resp->meta); |
200 | d->resp.statusCode = invalidHeader_GmStatusCode; | 193 | resp->statusCode = invalidHeader_GmStatusCode; |
201 | d->state = finished_GmRequestState; | 194 | d->state = finished_GmRequestState; |
202 | notifyDone = iTrue; | 195 | notifyDone = iTrue; |
203 | } | 196 | } |
204 | remove_Block(&d->resp.meta.chars, 0, 3); /* just the meta */ | 197 | remove_Block(&resp->meta.chars, 0, 3); /* just the meta */ |
205 | if (code == success_GmStatusCode && isEmpty_String(&d->resp.meta)) { | 198 | if (code == success_GmStatusCode && isEmpty_String(&resp->meta)) { |
206 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); /* default */ | 199 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); /* default */ |
207 | } | 200 | } |
208 | d->resp.statusCode = code; | 201 | resp->statusCode = code; |
209 | d->state = receivingBody_GmRequestState; | 202 | d->state = receivingBody_GmRequestState; |
210 | checkServerCertificate_GmRequest_(d); | 203 | checkServerCertificate_GmRequest_(d); |
211 | notifyUpdate = iTrue; | 204 | notifyUpdate = iTrue; |
212 | } | 205 | } |
213 | } | 206 | } |
214 | else if (d->state == receivingBody_GmRequestState) { | 207 | else if (d->state == receivingBody_GmRequestState) { |
215 | append_Block(&d->resp.body, data); | 208 | append_Block(&resp->body, data); |
216 | notifyUpdate = iTrue; | 209 | notifyUpdate = iTrue; |
217 | } | 210 | } |
218 | initCurrent_Time(&d->resp.when); | 211 | initCurrent_Time(&resp->when); |
219 | delete_Block(data); | 212 | delete_Block(data); |
220 | unlock_Mutex(&d->mutex); | 213 | unlock_Mutex(d->mtx); |
221 | if (notifyUpdate) { | 214 | const iBool allowed = exchange_Atomic(&d->allowUpdate, iFalse); |
215 | if (notifyUpdate && allowed) { | ||
222 | iNotifyAudience(d, updated, GmRequestUpdated); | 216 | iNotifyAudience(d, updated, GmRequestUpdated); |
223 | } | 217 | } |
224 | if (notifyDone) { | 218 | if (notifyDone) { |
@@ -228,21 +222,21 @@ static void readIncoming_GmRequest_(iGmRequest *d, iTlsRequest *req) { | |||
228 | 222 | ||
229 | static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) { | 223 | static void requestFinished_GmRequest_(iGmRequest *d, iTlsRequest *req) { |
230 | iAssert(req == d->req); | 224 | iAssert(req == d->req); |
231 | lock_Mutex(&d->mutex); | 225 | lock_Mutex(d->mtx); |
232 | /* There shouldn't be anything left to read. */ { | 226 | /* There shouldn't be anything left to read. */ { |
233 | iBlock *data = readAll_TlsRequest(req); | 227 | iBlock *data = readAll_TlsRequest(req); |
234 | iAssert(isEmpty_Block(data)); | 228 | iAssert(isEmpty_Block(data)); |
235 | delete_Block(data); | 229 | delete_Block(data); |
236 | initCurrent_Time(&d->resp.when); | 230 | initCurrent_Time(&d->resp->when); |
237 | } | 231 | } |
238 | d->state = (status_TlsRequest(req) == error_TlsRequestStatus ? failure_GmRequestState | 232 | d->state = (status_TlsRequest(req) == error_TlsRequestStatus ? failure_GmRequestState |
239 | : finished_GmRequestState); | 233 | : finished_GmRequestState); |
240 | if (d->state == failure_GmRequestState) { | 234 | if (d->state == failure_GmRequestState) { |
241 | d->resp.statusCode = tlsFailure_GmStatusCode; | 235 | d->resp->statusCode = tlsFailure_GmStatusCode; |
242 | set_String(&d->resp.meta, errorMessage_TlsRequest(req)); | 236 | set_String(&d->resp->meta, errorMessage_TlsRequest(req)); |
243 | } | 237 | } |
244 | checkServerCertificate_GmRequest_(d); | 238 | checkServerCertificate_GmRequest_(d); |
245 | unlock_Mutex(&d->mutex); | 239 | unlock_Mutex(d->mtx); |
246 | iNotifyAudience(d, finished, GmRequestFinished); | 240 | iNotifyAudience(d, finished, GmRequestFinished); |
247 | } | 241 | } |
248 | 242 | ||
@@ -349,14 +343,14 @@ static const iBlock *replaceVariables_(const iBlock *block) { | |||
349 | 343 | ||
350 | static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { | 344 | static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { |
351 | iBool notifyUpdate = iFalse; | 345 | iBool notifyUpdate = iFalse; |
352 | lock_Mutex(&d->mutex); | 346 | lock_Mutex(d->mtx); |
353 | d->resp.statusCode = success_GmStatusCode; | 347 | d->resp->statusCode = success_GmStatusCode; |
354 | iBlock *data = readAll_Socket(socket); | 348 | iBlock *data = readAll_Socket(socket); |
355 | if (!isEmpty_Block(data)) { | 349 | if (!isEmpty_Block(data)) { |
356 | processResponse_Gopher(&d->gopher, data); | 350 | processResponse_Gopher(&d->gopher, data); |
357 | } | 351 | } |
358 | delete_Block(data); | 352 | delete_Block(data); |
359 | unlock_Mutex(&d->mutex); | 353 | unlock_Mutex(d->mtx); |
360 | if (notifyUpdate) { | 354 | if (notifyUpdate) { |
361 | iNotifyAudience(d, updated, GmRequestUpdated); | 355 | iNotifyAudience(d, updated, GmRequestUpdated); |
362 | } | 356 | } |
@@ -365,12 +359,12 @@ static void gopherRead_GmRequest_(iGmRequest *d, iSocket *socket) { | |||
365 | static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { | 359 | static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { |
366 | iUnused(socket); | 360 | iUnused(socket); |
367 | iBool notify = iFalse; | 361 | iBool notify = iFalse; |
368 | lock_Mutex(&d->mutex); | 362 | lock_Mutex(d->mtx); |
369 | if (d->state != failure_GmRequestState) { | 363 | if (d->state != failure_GmRequestState) { |
370 | d->state = finished_GmRequestState; | 364 | d->state = finished_GmRequestState; |
371 | notify = iTrue; | 365 | notify = iTrue; |
372 | } | 366 | } |
373 | unlock_Mutex(&d->mutex); | 367 | unlock_Mutex(d->mtx); |
374 | if (notify) { | 368 | if (notify) { |
375 | iNotifyAudience(d, finished, GmRequestFinished); | 369 | iNotifyAudience(d, finished, GmRequestFinished); |
376 | } | 370 | } |
@@ -378,12 +372,12 @@ static void gopherDisconnected_GmRequest_(iGmRequest *d, iSocket *socket) { | |||
378 | 372 | ||
379 | static void gopherError_GmRequest_(iGmRequest *d, iSocket *socket, int error, const char *msg) { | 373 | static void gopherError_GmRequest_(iGmRequest *d, iSocket *socket, int error, const char *msg) { |
380 | iUnused(socket); | 374 | iUnused(socket); |
381 | lock_Mutex(&d->mutex); | 375 | lock_Mutex(d->mtx); |
382 | d->state = failure_GmRequestState; | 376 | d->state = failure_GmRequestState; |
383 | d->resp.statusCode = tlsFailure_GmStatusCode; | 377 | d->resp->statusCode = tlsFailure_GmStatusCode; |
384 | format_String(&d->resp.meta, "%s (errno %d)", msg, error); | 378 | format_String(&d->resp->meta, "%s (errno %d)", msg, error); |
385 | clear_Block(&d->resp.body); | 379 | clear_Block(&d->resp->body); |
386 | unlock_Mutex(&d->mutex); | 380 | unlock_Mutex(d->mtx); |
387 | iNotifyAudience(d, finished, GmRequestFinished); | 381 | iNotifyAudience(d, finished, GmRequestFinished); |
388 | } | 382 | } |
389 | 383 | ||
@@ -392,8 +386,9 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
392 | port = 70; /* default port */ | 386 | port = 70; /* default port */ |
393 | } | 387 | } |
394 | clear_Block(&d->gopher.source); | 388 | clear_Block(&d->gopher.source); |
395 | d->gopher.meta = &d->resp.meta; | 389 | iGmResponse *resp = d->resp; |
396 | d->gopher.output = &d->resp.body; | 390 | d->gopher.meta = &resp->meta; |
391 | d->gopher.output = &resp->body; | ||
397 | d->state = receivingBody_GmRequestState; | 392 | d->state = receivingBody_GmRequestState; |
398 | d->gopher.socket = new_Socket(cstr_String(host), port); | 393 | d->gopher.socket = new_Socket(cstr_String(host), port); |
399 | iConnect(Socket, d->gopher.socket, readyRead, d, gopherRead_GmRequest_); | 394 | iConnect(Socket, d->gopher.socket, readyRead, d, gopherRead_GmRequest_); |
@@ -401,8 +396,8 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
401 | iConnect(Socket, d->gopher.socket, error, d, gopherError_GmRequest_); | 396 | iConnect(Socket, d->gopher.socket, error, d, gopherError_GmRequest_); |
402 | open_Gopher(&d->gopher, &d->url); | 397 | open_Gopher(&d->gopher, &d->url); |
403 | if (d->gopher.needQueryArgs) { | 398 | if (d->gopher.needQueryArgs) { |
404 | d->resp.statusCode = input_GmStatusCode; | 399 | resp->statusCode = input_GmStatusCode; |
405 | setCStr_String(&d->resp.meta, "Enter query:"); | 400 | setCStr_String(&resp->meta, "Enter query:"); |
406 | d->state = finished_GmRequestState; | 401 | d->state = finished_GmRequestState; |
407 | iNotifyAudience(d, finished, GmRequestFinished); | 402 | iNotifyAudience(d, finished, GmRequestFinished); |
408 | } | 403 | } |
@@ -411,8 +406,10 @@ static void beginGopherConnection_GmRequest_(iGmRequest *d, const iString *host, | |||
411 | /*----------------------------------------------------------------------------------------------*/ | 406 | /*----------------------------------------------------------------------------------------------*/ |
412 | 407 | ||
413 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { | 408 | void init_GmRequest(iGmRequest *d, iGmCerts *certs) { |
414 | init_Mutex(&d->mutex); | 409 | d->mtx = new_Mutex(); |
415 | init_GmResponse(&d->resp); | 410 | d->resp = new_GmResponse(); |
411 | d->respLocked = iFalse; | ||
412 | set_Atomic(&d->allowUpdate, iTrue); | ||
416 | init_String(&d->url); | 413 | init_String(&d->url); |
417 | init_Gopher(&d->gopher); | 414 | init_Gopher(&d->gopher); |
418 | d->certs = certs; | 415 | d->certs = certs; |
@@ -427,22 +424,24 @@ void deinit_GmRequest(iGmRequest *d) { | |||
427 | iDisconnectObject(TlsRequest, d->req, readyRead, d); | 424 | iDisconnectObject(TlsRequest, d->req, readyRead, d); |
428 | iDisconnectObject(TlsRequest, d->req, finished, d); | 425 | iDisconnectObject(TlsRequest, d->req, finished, d); |
429 | } | 426 | } |
430 | lock_Mutex(&d->mutex); | 427 | lock_Mutex(d->mtx); |
431 | if (!isFinished_GmRequest(d)) { | 428 | if (!isFinished_GmRequest(d)) { |
432 | unlock_Mutex(&d->mutex); | 429 | unlock_Mutex(d->mtx); |
433 | cancel_GmRequest(d); | 430 | cancel_GmRequest(d); |
434 | d->state = finished_GmRequestState; | 431 | d->state = finished_GmRequestState; |
435 | } | 432 | } |
436 | else { | 433 | else { |
437 | unlock_Mutex(&d->mutex); | 434 | unlock_Mutex(d->mtx); |
438 | } | 435 | } |
439 | iReleasePtr(&d->req); | 436 | iReleasePtr(&d->req); |
440 | deinit_Gopher(&d->gopher); | 437 | deinit_Gopher(&d->gopher); |
441 | delete_Audience(d->finished); | 438 | delete_Audience(d->finished); |
442 | delete_Audience(d->updated); | 439 | delete_Audience(d->updated); |
443 | deinit_GmResponse(&d->resp); | 440 | // delete_GmResponse(d->respPub); |
441 | // deinit_GmResponse(&d->respInt); | ||
442 | delete_GmResponse(d->resp); | ||
444 | deinit_String(&d->url); | 443 | deinit_String(&d->url); |
445 | deinit_Mutex(&d->mutex); | 444 | delete_Mutex(d->mtx); |
446 | } | 445 | } |
447 | 446 | ||
448 | void setUrl_GmRequest(iGmRequest *d, const iString *url) { | 447 | void setUrl_GmRequest(iGmRequest *d, const iString *url) { |
@@ -455,7 +454,9 @@ void submit_GmRequest(iGmRequest *d) { | |||
455 | if (d->state != initialized_GmRequestState) { | 454 | if (d->state != initialized_GmRequestState) { |
456 | return; | 455 | return; |
457 | } | 456 | } |
458 | clear_GmResponse(&d->resp); | 457 | set_Atomic(&d->allowUpdate, iTrue); |
458 | iGmResponse *resp = d->resp; | ||
459 | clear_GmResponse(resp); | ||
459 | iUrl url; | 460 | iUrl url; |
460 | init_Url(&url, &d->url); | 461 | init_Url(&url, &d->url); |
461 | /* Check for special schemes. */ | 462 | /* Check for special schemes. */ |
@@ -466,14 +467,14 @@ void submit_GmRequest(iGmRequest *d) { | |||
466 | if (equalCase_Rangecc(url.scheme, "about")) { | 467 | if (equalCase_Rangecc(url.scheme, "about")) { |
467 | const iBlock *src = aboutPageSource_(url.path); | 468 | const iBlock *src = aboutPageSource_(url.path); |
468 | if (src) { | 469 | if (src) { |
469 | d->resp.statusCode = success_GmStatusCode; | 470 | resp->statusCode = success_GmStatusCode; |
470 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); | 471 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); |
471 | set_Block(&d->resp.body, replaceVariables_(src)); | 472 | set_Block(&resp->body, replaceVariables_(src)); |
472 | d->state = receivingBody_GmRequestState; | 473 | d->state = receivingBody_GmRequestState; |
473 | iNotifyAudience(d, updated, GmRequestUpdated); | 474 | iNotifyAudience(d, updated, GmRequestUpdated); |
474 | } | 475 | } |
475 | else { | 476 | else { |
476 | d->resp.statusCode = invalidLocalResource_GmStatusCode; | 477 | resp->statusCode = invalidLocalResource_GmStatusCode; |
477 | } | 478 | } |
478 | d->state = finished_GmRequestState; | 479 | d->state = finished_GmRequestState; |
479 | iNotifyAudience(d, finished, GmRequestFinished); | 480 | iNotifyAudience(d, finished, GmRequestFinished); |
@@ -485,41 +486,41 @@ void submit_GmRequest(iGmRequest *d) { | |||
485 | if (open_File(f, readOnly_FileMode)) { | 486 | if (open_File(f, readOnly_FileMode)) { |
486 | /* TODO: Check supported file types: images, audio */ | 487 | /* TODO: Check supported file types: images, audio */ |
487 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ | 488 | /* TODO: Detect text files based on contents? E.g., is the content valid UTF-8. */ |
488 | d->resp.statusCode = success_GmStatusCode; | 489 | resp->statusCode = success_GmStatusCode; |
489 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { | 490 | if (endsWithCase_String(path, ".gmi") || endsWithCase_String(path, ".gemini")) { |
490 | setCStr_String(&d->resp.meta, "text/gemini; charset=utf-8"); | 491 | setCStr_String(&resp->meta, "text/gemini; charset=utf-8"); |
491 | } | 492 | } |
492 | else if (endsWithCase_String(path, ".txt")) { | 493 | else if (endsWithCase_String(path, ".txt")) { |
493 | setCStr_String(&d->resp.meta, "text/plain"); | 494 | setCStr_String(&resp->meta, "text/plain"); |
494 | } | 495 | } |
495 | else if (endsWithCase_String(path, ".png")) { | 496 | else if (endsWithCase_String(path, ".png")) { |
496 | setCStr_String(&d->resp.meta, "image/png"); | 497 | setCStr_String(&resp->meta, "image/png"); |
497 | } | 498 | } |
498 | else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { | 499 | else if (endsWithCase_String(path, ".jpg") || endsWithCase_String(path, ".jpeg")) { |
499 | setCStr_String(&d->resp.meta, "image/jpeg"); | 500 | setCStr_String(&resp->meta, "image/jpeg"); |
500 | } | 501 | } |
501 | else if (endsWithCase_String(path, ".gif")) { | 502 | else if (endsWithCase_String(path, ".gif")) { |
502 | setCStr_String(&d->resp.meta, "image/gif"); | 503 | setCStr_String(&resp->meta, "image/gif"); |
503 | } | 504 | } |
504 | else if (endsWithCase_String(path, ".wav")) { | 505 | else if (endsWithCase_String(path, ".wav")) { |
505 | setCStr_String(&d->resp.meta, "audio/wave"); | 506 | setCStr_String(&resp->meta, "audio/wave"); |
506 | } | 507 | } |
507 | else if (endsWithCase_String(path, ".ogg")) { | 508 | else if (endsWithCase_String(path, ".ogg")) { |
508 | setCStr_String(&d->resp.meta, "audio/ogg"); | 509 | setCStr_String(&resp->meta, "audio/ogg"); |
509 | } | 510 | } |
510 | else if (endsWithCase_String(path, ".mp3")) { | 511 | else if (endsWithCase_String(path, ".mp3")) { |
511 | setCStr_String(&d->resp.meta, "audio/mpeg"); | 512 | setCStr_String(&resp->meta, "audio/mpeg"); |
512 | } | 513 | } |
513 | else { | 514 | else { |
514 | setCStr_String(&d->resp.meta, "application/octet-stream"); | 515 | setCStr_String(&resp->meta, "application/octet-stream"); |
515 | } | 516 | } |
516 | set_Block(&d->resp.body, collect_Block(readAll_File(f))); | 517 | set_Block(&resp->body, collect_Block(readAll_File(f))); |
517 | d->state = receivingBody_GmRequestState; | 518 | d->state = receivingBody_GmRequestState; |
518 | iNotifyAudience(d, updated, GmRequestUpdated); | 519 | iNotifyAudience(d, updated, GmRequestUpdated); |
519 | } | 520 | } |
520 | else { | 521 | else { |
521 | d->resp.statusCode = failedToOpenFile_GmStatusCode; | 522 | resp->statusCode = failedToOpenFile_GmStatusCode; |
522 | setCStr_String(&d->resp.meta, cstr_String(path)); | 523 | setCStr_String(&resp->meta, cstr_String(path)); |
523 | } | 524 | } |
524 | iRelease(f); | 525 | iRelease(f); |
525 | d->state = finished_GmRequestState; | 526 | d->state = finished_GmRequestState; |
@@ -527,14 +528,14 @@ void submit_GmRequest(iGmRequest *d) { | |||
527 | return; | 528 | return; |
528 | } | 529 | } |
529 | else if (equalCase_Rangecc(url.scheme, "data")) { | 530 | else if (equalCase_Rangecc(url.scheme, "data")) { |
530 | d->resp.statusCode = success_GmStatusCode; | 531 | resp->statusCode = success_GmStatusCode; |
531 | iString *src = collectNewCStr_String(url.scheme.start + 5); | 532 | iString *src = collectNewCStr_String(url.scheme.start + 5); |
532 | iRangecc header = { constBegin_String(src), constBegin_String(src) }; | 533 | iRangecc header = { constBegin_String(src), constBegin_String(src) }; |
533 | while (header.end < constEnd_String(src) && *header.end != ',') { | 534 | while (header.end < constEnd_String(src) && *header.end != ',') { |
534 | header.end++; | 535 | header.end++; |
535 | } | 536 | } |
536 | iBool isBase64 = iFalse; | 537 | iBool isBase64 = iFalse; |
537 | setRange_String(&d->resp.meta, header); | 538 | setRange_String(&resp->meta, header); |
538 | /* Check what's in the header. */ { | 539 | /* Check what's in the header. */ { |
539 | iRangecc entry = iNullRange; | 540 | iRangecc entry = iNullRange; |
540 | while (nextSplit_Rangecc(header, ";", &entry)) { | 541 | while (nextSplit_Rangecc(header, ";", &entry)) { |
@@ -550,7 +551,7 @@ void submit_GmRequest(iGmRequest *d) { | |||
550 | else { | 551 | else { |
551 | set_String(src, collect_String(urlDecode_String(src))); | 552 | set_String(src, collect_String(urlDecode_String(src))); |
552 | } | 553 | } |
553 | set_Block(&d->resp.body, &src->chars); | 554 | set_Block(&resp->body, &src->chars); |
554 | d->state = receivingBody_GmRequestState; | 555 | d->state = receivingBody_GmRequestState; |
555 | iNotifyAudience(d, updated, GmRequestUpdated); | 556 | iNotifyAudience(d, updated, GmRequestUpdated); |
556 | d->state = finished_GmRequestState; | 557 | d->state = finished_GmRequestState; |
@@ -575,7 +576,7 @@ void submit_GmRequest(iGmRequest *d) { | |||
575 | return; | 576 | return; |
576 | } | 577 | } |
577 | else if (!equalCase_Rangecc(url.scheme, "gemini")) { | 578 | else if (!equalCase_Rangecc(url.scheme, "gemini")) { |
578 | d->resp.statusCode = unsupportedProtocol_GmStatusCode; | 579 | resp->statusCode = unsupportedProtocol_GmStatusCode; |
579 | d->state = finished_GmRequestState; | 580 | d->state = finished_GmRequestState; |
580 | iNotifyAudience(d, finished, GmRequestFinished); | 581 | iNotifyAudience(d, finished, GmRequestFinished); |
581 | return; | 582 | return; |
@@ -604,45 +605,63 @@ void cancel_GmRequest(iGmRequest *d) { | |||
604 | cancel_Gopher(&d->gopher); | 605 | cancel_Gopher(&d->gopher); |
605 | } | 606 | } |
606 | 607 | ||
608 | iGmResponse *lockResponse_GmRequest(iGmRequest *d) { | ||
609 | iAssert(!d->respLocked); | ||
610 | lock_Mutex(d->mtx); | ||
611 | d->respLocked = iTrue; | ||
612 | return d->resp; | ||
613 | } | ||
614 | |||
615 | void unlockResponse_GmRequest(iGmRequest *d) { | ||
616 | iAssert(d->respLocked); | ||
617 | d->respLocked = iFalse; | ||
618 | set_Atomic(&d->allowUpdate, iTrue); | ||
619 | unlock_Mutex(d->mtx); | ||
620 | } | ||
621 | |||
607 | iBool isFinished_GmRequest(const iGmRequest *d) { | 622 | iBool isFinished_GmRequest(const iGmRequest *d) { |
608 | iBool done; | 623 | iBool done; |
609 | iGuardMutex(&d->mutex, | 624 | iGuardMutex(d->mtx, |
610 | done = (d->state == finished_GmRequestState || d->state == failure_GmRequestState)); | 625 | done = (d->state == finished_GmRequestState || d->state == failure_GmRequestState)); |
611 | return done; | 626 | return done; |
612 | } | 627 | } |
613 | 628 | ||
614 | enum iGmStatusCode status_GmRequest(const iGmRequest *d) { | 629 | enum iGmStatusCode status_GmRequest(const iGmRequest *d) { |
615 | return d->resp.statusCode; | 630 | enum iGmStatusCode code; |
631 | iGuardMutex(d->mtx, code = d->resp->statusCode); | ||
632 | return code; | ||
616 | } | 633 | } |
617 | 634 | ||
618 | const iString *meta_GmRequest(const iGmRequest *d) { | 635 | const iString *meta_GmRequest(const iGmRequest *d) { |
619 | if (d->state >= receivingBody_GmRequestState) { | 636 | iAssert(isFinished_GmRequest(d)); |
620 | return &d->resp.meta; | 637 | return &d->resp->meta; |
621 | } | ||
622 | return collectNew_String(); | ||
623 | } | 638 | } |
624 | 639 | ||
625 | const iBlock *body_GmRequest(const iGmRequest *d) { | 640 | const iBlock *body_GmRequest(const iGmRequest *d) { |
626 | iBlock *body; | 641 | iAssert(isFinished_GmRequest(d)); |
627 | iGuardMutex(&d->mutex, body = collect_Block(copy_Block(&d->resp.body))); | 642 | return &d->resp->body; |
628 | return body; | ||
629 | } | 643 | } |
630 | 644 | ||
631 | const iString *url_GmRequest(const iGmRequest *d) { | 645 | size_t bodySize_GmRequest(const iGmRequest *d) { |
632 | return &d->url; | 646 | size_t size; |
647 | iGuardMutex(d->mtx, size = size_Block(&d->resp->body)); | ||
648 | return size; | ||
633 | } | 649 | } |
634 | 650 | ||
635 | const iGmResponse *response_GmRequest(const iGmRequest *d) { | 651 | const iString *url_GmRequest(const iGmRequest *d) { |
636 | iAssert(d->state != initialized_GmRequestState); | 652 | return &d->url; |
637 | return &d->resp; | ||
638 | } | 653 | } |
639 | 654 | ||
640 | int certFlags_GmRequest(const iGmRequest *d) { | 655 | int certFlags_GmRequest(const iGmRequest *d) { |
641 | return d->resp.certFlags; | 656 | int flags; |
657 | iGuardMutex(d->mtx, flags = d->resp->certFlags); | ||
658 | return flags; | ||
642 | } | 659 | } |
643 | 660 | ||
644 | iDate certExpirationDate_GmRequest(const iGmRequest *d) { | 661 | iDate certExpirationDate_GmRequest(const iGmRequest *d) { |
645 | return d->resp.certValidUntil; | 662 | iDate expr; |
663 | iGuardMutex(d->mtx, expr = d->resp->certValidUntil); | ||
664 | return expr; | ||
646 | } | 665 | } |
647 | 666 | ||
648 | iDefineClass(GmRequest) | 667 | iDefineClass(GmRequest) |
diff --git a/src/gmrequest.h b/src/gmrequest.h index 31059a44..bd340cf1 100644 --- a/src/gmrequest.h +++ b/src/gmrequest.h | |||
@@ -68,12 +68,15 @@ void setUrl_GmRequest (iGmRequest *, const iString *ur | |||
68 | void submit_GmRequest (iGmRequest *); | 68 | void submit_GmRequest (iGmRequest *); |
69 | void cancel_GmRequest (iGmRequest *); | 69 | void cancel_GmRequest (iGmRequest *); |
70 | 70 | ||
71 | iGmResponse * lockResponse_GmRequest (iGmRequest *); | ||
72 | void unlockResponse_GmRequest (iGmRequest *); | ||
73 | |||
71 | iBool isFinished_GmRequest (const iGmRequest *); | 74 | iBool isFinished_GmRequest (const iGmRequest *); |
72 | enum iGmStatusCode status_GmRequest (const iGmRequest *); | 75 | enum iGmStatusCode status_GmRequest (const iGmRequest *); |
73 | const iString * meta_GmRequest (const iGmRequest *); | 76 | const iString * meta_GmRequest (const iGmRequest *); |
74 | const iBlock * body_GmRequest (const iGmRequest *); | 77 | const iBlock * body_GmRequest (const iGmRequest *); |
78 | size_t bodySize_GmRequest (const iGmRequest *); | ||
75 | const iString * url_GmRequest (const iGmRequest *); | 79 | const iString * url_GmRequest (const iGmRequest *); |
76 | const iGmResponse * response_GmRequest (const iGmRequest *); | ||
77 | 80 | ||
78 | int certFlags_GmRequest (const iGmRequest *); | 81 | int certFlags_GmRequest (const iGmRequest *); |
79 | iDate certExpirationDate_GmRequest(const iGmRequest *); | 82 | iDate certExpirationDate_GmRequest(const iGmRequest *); |
diff --git a/src/media.c b/src/media.c index cd3dfb82..8bd635a5 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -154,12 +154,13 @@ void clear_Media(iMedia *d) { | |||
154 | clear_PtrArray(&d->audio); | 154 | clear_PtrArray(&d->audio); |
155 | } | 155 | } |
156 | 156 | ||
157 | void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, | 157 | iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, |
158 | int flags) { | 158 | int flags) { |
159 | const iBool isPartial = (flags & partialData_MediaFlag) != 0; | 159 | const iBool isPartial = (flags & partialData_MediaFlag) != 0; |
160 | const iBool allowHide = (flags & allowHide_MediaFlag) != 0; | 160 | const iBool allowHide = (flags & allowHide_MediaFlag) != 0; |
161 | const iBool isDeleting = (!mime || !data); | 161 | const iBool isDeleting = (!mime || !data); |
162 | iMediaId existing = findLinkImage_Media(d, linkId); | 162 | iMediaId existing = findLinkImage_Media(d, linkId); |
163 | iBool isNew = iFalse; | ||
163 | if (existing) { | 164 | if (existing) { |
164 | iGmImage *img; | 165 | iGmImage *img; |
165 | if (isDeleting) { | 166 | if (isDeleting) { |
@@ -205,6 +206,7 @@ void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBloc | |||
205 | if (!isPartial) { | 206 | if (!isPartial) { |
206 | makeTexture_GmImage(img); | 207 | makeTexture_GmImage(img); |
207 | } | 208 | } |
209 | isNew = iTrue; | ||
208 | } | 210 | } |
209 | else if (startsWith_String(mime, "audio/")) { | 211 | else if (startsWith_String(mime, "audio/")) { |
210 | iGmAudio *audio = new_GmAudio(); | 212 | iGmAudio *audio = new_GmAudio(); |
@@ -219,8 +221,10 @@ void setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBloc | |||
219 | /* Start playing right away. */ | 221 | /* Start playing right away. */ |
220 | start_Player(audio->player); | 222 | start_Player(audio->player); |
221 | postCommandf_App("media.player.started player:%p", audio->player); | 223 | postCommandf_App("media.player.started player:%p", audio->player); |
224 | isNew = iTrue; | ||
222 | } | 225 | } |
223 | } | 226 | } |
227 | return isNew; | ||
224 | } | 228 | } |
225 | 229 | ||
226 | iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { | 230 | iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) { |
diff --git a/src/media.h b/src/media.h index 12334936..ddaa2d3c 100644 --- a/src/media.h +++ b/src/media.h | |||
@@ -54,7 +54,7 @@ enum iMediaFlags { | |||
54 | }; | 54 | }; |
55 | 55 | ||
56 | void clear_Media (iMedia *); | 56 | void clear_Media (iMedia *); |
57 | void setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); | 57 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); |
58 | 58 | ||
59 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); | 59 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); |
60 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); | 60 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d7d15a67..a843e840 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -766,7 +766,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
766 | 766 | ||
767 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { | 767 | static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { |
768 | iLabelWidget *prog = findWidget_App("document.progress"); | 768 | iLabelWidget *prog = findWidget_App("document.progress"); |
769 | const size_t dlSize = d->request ? size_Block(body_GmRequest(d->request)) : 0; | 769 | const size_t dlSize = d->request ? bodySize_GmRequest(d->request) : 0; |
770 | setFlags_Widget(as_Widget(prog), hidden_WidgetFlag, dlSize < 250000); | 770 | setFlags_Widget(as_Widget(prog), hidden_WidgetFlag, dlSize < 250000); |
771 | if (isVisible_Widget(prog)) { | 771 | if (isVisible_Widget(prog)) { |
772 | updateText_LabelWidget(prog, | 772 | updateText_LabelWidget(prog, |
@@ -1032,9 +1032,10 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1032 | if (statusCode == none_GmStatusCode) { | 1032 | if (statusCode == none_GmStatusCode) { |
1033 | return; | 1033 | return; |
1034 | } | 1034 | } |
1035 | iGmResponse *resp = lockResponse_GmRequest(d->request); | ||
1035 | if (d->state == fetching_RequestState) { | 1036 | if (d->state == fetching_RequestState) { |
1036 | d->state = receivedPartialResponse_RequestState; | 1037 | d->state = receivedPartialResponse_RequestState; |
1037 | updateTrust_DocumentWidget_(d, response_GmRequest(d->request)); | 1038 | updateTrust_DocumentWidget_(d, resp); |
1038 | init_Anim(&d->sideOpacity, 0); | 1039 | init_Anim(&d->sideOpacity, 0); |
1039 | switch (category_GmStatusCode(statusCode)) { | 1040 | switch (category_GmStatusCode(statusCode)) { |
1040 | case categoryInput_GmStatusCode: { | 1041 | case categoryInput_GmStatusCode: { |
@@ -1045,9 +1046,9 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1045 | as_Widget(d), | 1046 | as_Widget(d), |
1046 | NULL, | 1047 | NULL, |
1047 | format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)), | 1048 | format_CStr(uiHeading_ColorEscape "%s", cstr_Rangecc(parts.host)), |
1048 | isEmpty_String(meta_GmRequest(d->request)) | 1049 | isEmpty_String(&resp->meta) |
1049 | ? format_CStr("Please enter input for %s:", cstr_Rangecc(parts.path)) | 1050 | ? format_CStr("Please enter input for %s:", cstr_Rangecc(parts.path)) |
1050 | : cstr_String(meta_GmRequest(d->request)), | 1051 | : cstr_String(&resp->meta), |
1051 | uiTextCaution_ColorEscape "Send \u21d2", | 1052 | uiTextCaution_ColorEscape "Send \u21d2", |
1052 | "document.input.submit"); | 1053 | "document.input.submit"); |
1053 | setSensitive_InputWidget(findChild_Widget(dlg, "input"), | 1054 | setSensitive_InputWidget(findChild_Widget(dlg, "input"), |
@@ -1057,15 +1058,15 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1057 | case categorySuccess_GmStatusCode: | 1058 | case categorySuccess_GmStatusCode: |
1058 | init_Anim(&d->scrollY, 0); | 1059 | init_Anim(&d->scrollY, 0); |
1059 | reset_GmDocument(d->doc); /* new content incoming */ | 1060 | reset_GmDocument(d->doc); /* new content incoming */ |
1060 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request), iTrue); | 1061 | updateDocument_DocumentWidget_(d, resp, iTrue); |
1061 | break; | 1062 | break; |
1062 | case categoryRedirect_GmStatusCode: | 1063 | case categoryRedirect_GmStatusCode: |
1063 | if (isEmpty_String(meta_GmRequest(d->request))) { | 1064 | if (isEmpty_String(&resp->meta)) { |
1064 | showErrorPage_DocumentWidget_(d, invalidRedirect_GmStatusCode, NULL); | 1065 | showErrorPage_DocumentWidget_(d, invalidRedirect_GmStatusCode, NULL); |
1065 | } | 1066 | } |
1066 | else { | 1067 | else { |
1067 | /* Only accept redirects that use gemini scheme. */ | 1068 | /* Only accept redirects that use gemini scheme. */ |
1068 | const iString *dstUrl = absoluteUrl_String(d->mod.url, meta_GmRequest(d->request)); | 1069 | const iString *dstUrl = absoluteUrl_String(d->mod.url, &resp->meta); |
1069 | if (d->redirectCount >= 5) { | 1070 | if (d->redirectCount >= 5) { |
1070 | showErrorPage_DocumentWidget_(d, tooManyRedirects_GmStatusCode, dstUrl); | 1071 | showErrorPage_DocumentWidget_(d, tooManyRedirects_GmStatusCode, dstUrl); |
1071 | } | 1072 | } |
@@ -1085,17 +1086,17 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1085 | break; | 1086 | break; |
1086 | default: | 1087 | default: |
1087 | if (isDefined_GmError(statusCode)) { | 1088 | if (isDefined_GmError(statusCode)) { |
1088 | showErrorPage_DocumentWidget_(d, statusCode, meta_GmRequest(d->request)); | 1089 | showErrorPage_DocumentWidget_(d, statusCode, &resp->meta); |
1089 | } | 1090 | } |
1090 | else if (category_GmStatusCode(statusCode) == | 1091 | else if (category_GmStatusCode(statusCode) == |
1091 | categoryTemporaryFailure_GmStatusCode) { | 1092 | categoryTemporaryFailure_GmStatusCode) { |
1092 | showErrorPage_DocumentWidget_( | 1093 | showErrorPage_DocumentWidget_( |
1093 | d, temporaryFailure_GmStatusCode, meta_GmRequest(d->request)); | 1094 | d, temporaryFailure_GmStatusCode, &resp->meta); |
1094 | } | 1095 | } |
1095 | else if (category_GmStatusCode(statusCode) == | 1096 | else if (category_GmStatusCode(statusCode) == |
1096 | categoryPermanentFailure_GmStatusCode) { | 1097 | categoryPermanentFailure_GmStatusCode) { |
1097 | showErrorPage_DocumentWidget_( | 1098 | showErrorPage_DocumentWidget_( |
1098 | d, permanentFailure_GmStatusCode, meta_GmRequest(d->request)); | 1099 | d, permanentFailure_GmStatusCode, &resp->meta); |
1099 | } | 1100 | } |
1100 | break; | 1101 | break; |
1101 | } | 1102 | } |
@@ -1104,12 +1105,13 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1104 | switch (category_GmStatusCode(statusCode)) { | 1105 | switch (category_GmStatusCode(statusCode)) { |
1105 | case categorySuccess_GmStatusCode: | 1106 | case categorySuccess_GmStatusCode: |
1106 | /* More content available. */ | 1107 | /* More content available. */ |
1107 | updateDocument_DocumentWidget_(d, response_GmRequest(d->request), iFalse); | 1108 | updateDocument_DocumentWidget_(d, resp, iFalse); |
1108 | break; | 1109 | break; |
1109 | default: | 1110 | default: |
1110 | break; | 1111 | break; |
1111 | } | 1112 | } |
1112 | } | 1113 | } |
1114 | unlockResponse_GmRequest(d->request); | ||
1113 | } | 1115 | } |
1114 | 1116 | ||
1115 | static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 1117 | static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { |
@@ -1190,18 +1192,21 @@ static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char * | |||
1190 | /* Pass new data to media players. */ | 1192 | /* Pass new data to media players. */ |
1191 | const enum iGmStatusCode code = status_GmRequest(req->req); | 1193 | const enum iGmStatusCode code = status_GmRequest(req->req); |
1192 | if (isSuccess_GmStatusCode(code)) { | 1194 | if (isSuccess_GmStatusCode(code)) { |
1193 | if (startsWith_String(meta_GmRequest(req->req), "audio/")) { | 1195 | iGmResponse *resp = lockResponse_GmRequest(req->req); |
1196 | if (startsWith_String(&resp->meta, "audio/")) { | ||
1194 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ | 1197 | /* TODO: Use a helper? This is same as below except for the partialData flag. */ |
1195 | setData_Media(media_GmDocument(d->doc), | 1198 | if (setData_Media(media_GmDocument(d->doc), |
1196 | req->linkId, | 1199 | req->linkId, |
1197 | meta_GmRequest(req->req), | 1200 | &resp->meta, |
1198 | body_GmRequest(req->req), | 1201 | &resp->body, |
1199 | partialData_MediaFlag | allowHide_MediaFlag); | 1202 | partialData_MediaFlag | allowHide_MediaFlag)) { |
1200 | redoLayout_GmDocument(d->doc); | 1203 | redoLayout_GmDocument(d->doc); |
1204 | } | ||
1201 | updateVisible_DocumentWidget_(d); | 1205 | updateVisible_DocumentWidget_(d); |
1202 | invalidate_DocumentWidget_(d); | 1206 | invalidate_DocumentWidget_(d); |
1203 | refresh_Widget(as_Widget(d)); | 1207 | refresh_Widget(as_Widget(d)); |
1204 | } | 1208 | } |
1209 | unlockResponse_GmRequest(req->req); | ||
1205 | } | 1210 | } |
1206 | /* Update the link's progress. */ | 1211 | /* Update the link's progress. */ |
1207 | invalidateLink_DocumentWidget_(d, req->linkId); | 1212 | invalidateLink_DocumentWidget_(d, req->linkId); |
@@ -1482,8 +1487,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1482 | return iTrue; | 1487 | return iTrue; |
1483 | } | 1488 | } |
1484 | else if (equalWidget_Command(cmd, w, "document.request.updated") && | 1489 | else if (equalWidget_Command(cmd, w, "document.request.updated") && |
1485 | pointerLabel_Command(cmd, "request") == d->request) { | 1490 | d->request && pointerLabel_Command(cmd, "request") == d->request) { |
1486 | set_Block(&d->sourceContent, body_GmRequest(d->request)); | 1491 | set_Block(&d->sourceContent, &lockResponse_GmRequest(d->request)->body); |
1492 | unlockResponse_GmRequest(d->request); | ||
1487 | if (document_App() == d) { | 1493 | if (document_App() == d) { |
1488 | updateFetchProgress_DocumentWidget_(d); | 1494 | updateFetchProgress_DocumentWidget_(d); |
1489 | } | 1495 | } |
@@ -1501,7 +1507,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
1501 | /* The response may be cached. */ { | 1507 | /* The response may be cached. */ { |
1502 | if (!equal_Rangecc(urlScheme_String(d->mod.url), "about") && | 1508 | if (!equal_Rangecc(urlScheme_String(d->mod.url), "about") && |
1503 | startsWithCase_String(meta_GmRequest(d->request), "text/")) { | 1509 | startsWithCase_String(meta_GmRequest(d->request), "text/")) { |
1504 | setCachedResponse_History(d->mod.history, response_GmRequest(d->request)); | 1510 | setCachedResponse_History(d->mod.history, lockResponse_GmRequest(d->request)); |
1511 | unlockResponse_GmRequest(d->request); | ||
1505 | } | 1512 | } |
1506 | } | 1513 | } |
1507 | iReleasePtr(&d->request); | 1514 | iReleasePtr(&d->request); |
@@ -2496,7 +2503,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
2496 | topRight_Rect(linkRect), | 2503 | topRight_Rect(linkRect), |
2497 | tmInlineContentMetadata_ColorId, | 2504 | tmInlineContentMetadata_ColorId, |
2498 | " \u2014 Fetching\u2026 (%.1f MB)", | 2505 | " \u2014 Fetching\u2026 (%.1f MB)", |
2499 | (float) size_Block(body_GmRequest(mr->req)) / 1.0e6f); | 2506 | (float) bodySize_GmRequest(mr->req) / 1.0e6f); |
2500 | } | 2507 | } |
2501 | } | 2508 | } |
2502 | else if (isHover) { | 2509 | else if (isHover) { |