summaryrefslogtreecommitdiff
path: root/src/gmrequest.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gmrequest.c')
-rw-r--r--src/gmrequest.c152
1 files changed, 133 insertions, 19 deletions
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 0d69861d..137e8303 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -1,8 +1,31 @@
1/* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi>
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are met:
5
61. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
82. Redistributions in binary form must reproduce the above copyright notice,
9 this list of conditions and the following disclaimer in the documentation
10 and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
1#include "gmrequest.h" 23#include "gmrequest.h"
2#include "gmutil.h" 24#include "gmutil.h"
3#include "gmcerts.h" 25#include "gmcerts.h"
4#include "app.h" /* dataDir_App() */ 26#include "app.h" /* dataDir_App() */
5#include "embedded.h" 27#include "embedded.h"
28#include "ui/text.h"
6 29
7#include <the_Foundation/file.h> 30#include <the_Foundation/file.h>
8#include <the_Foundation/mutex.h> 31#include <the_Foundation/mutex.h>
@@ -171,11 +194,22 @@ static void checkServerCertificate_GmRequest_(iGmRequest *d) {
171 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req); 194 const iTlsCertificate *cert = serverCertificate_TlsRequest(d->req);
172 d->resp.certFlags = 0; 195 d->resp.certFlags = 0;
173 if (cert) { 196 if (cert) {
174 const iRangecc domain = urlHost_String(&d->url); 197 const iRangecc domain = range_String(hostName_Address(address_TlsRequest(d->req)));
175 d->resp.certFlags |= available_GmCertFlag; 198 d->resp.certFlags |= available_GmCertFlag;
176 if (!isExpired_TlsCertificate(cert)) { 199 if (!isExpired_TlsCertificate(cert)) {
177 d->resp.certFlags |= timeVerified_GmCertFlag; 200 d->resp.certFlags |= timeVerified_GmCertFlag;
178 } 201 }
202 /* TODO: Check for IP too (see below), because it may be specified in the SAN. */
203#if 0
204 iString *ip = toStringFlags_Address(address_TlsRequest(d->req), noPort_SocketStringFlag, 0);
205 if (verifyIp_TlsCertificate(cert, ip)) {
206 printf("[GmRequest] IP address %s matches!\n", cstr_String(ip));
207 }
208 else {
209 printf("[GmRequest] IP address %s not matched\n", cstr_String(ip));
210 }
211 delete_String(ip);
212#endif
179 if (verifyDomain_TlsCertificate(cert, domain)) { 213 if (verifyDomain_TlsCertificate(cert, domain)) {
180 d->resp.certFlags |= domainVerified_GmCertFlag; 214 d->resp.certFlags |= domainVerified_GmCertFlag;
181 } 215 }
@@ -265,31 +299,92 @@ static void requestFinished_GmRequest_(iAnyObject *obj) {
265 299
266static const iBlock *aboutPageSource_(iRangecc path) { 300static const iBlock *aboutPageSource_(iRangecc path) {
267 const iBlock *src = NULL; 301 const iBlock *src = NULL;
268 if (equalCase_Rangecc(&path, "lagrange")) { 302 if (equalCase_Rangecc(path, "lagrange")) {
269 return &blobAbout_Embedded; 303 return &blobLagrange_Embedded;
270 } 304 }
271 if (equalCase_Rangecc(&path, "help")) { 305 if (equalCase_Rangecc(path, "help")) {
272 return &blobHelp_Embedded; 306 return &blobHelp_Embedded;
273 } 307 }
274 if (equalCase_Rangecc(&path, "version")) { 308 if (equalCase_Rangecc(path, "version")) {
275 return &blobVersion_Embedded; 309 return &blobVersion_Embedded;
276 } 310 }
277 return src; 311 return src;
278} 312}
279 313
280static const iBlock *replaceVariables_(const iBlock *block) { 314static const iBlock *replaceVariables_(const iBlock *block) {
281 iRegExp *var = new_RegExp("\\$\\{([A-Z_]+)\\}", 0); 315 iRegExp *var = new_RegExp("\\$\\{([^}]+)\\}", 0);
282 iRegExpMatch m; 316 iRegExpMatch m;
317 init_RegExpMatch(&m);
283 if (matchRange_RegExp(var, range_Block(block), &m)) { 318 if (matchRange_RegExp(var, range_Block(block), &m)) {
284 iBlock *replaced = collect_Block(copy_Block(block)); 319 iBlock *replaced = collect_Block(copy_Block(block));
285 do { 320 do {
286 const iRangei span = m.range; 321 const iRangei span = m.range;
287 remove_Block(replaced, span.start, size_Range(&span));
288 const iRangecc name = capturedRange_RegExpMatch(&m, 1); 322 const iRangecc name = capturedRange_RegExpMatch(&m, 1);
289 if (equal_Rangecc(&name, "APP_VERSION")) { 323 iRangecc repl = iNullRange;
290 insertData_Block(replaced, span.start, 324 if (equal_Rangecc(name, "APP_VERSION")) {
291 LAGRANGE_APP_VERSION, strlen(LAGRANGE_APP_VERSION)); 325 repl = range_CStr(LAGRANGE_APP_VERSION);
326 }
327 else if (startsWith_Rangecc(name, "BT:")) { /* block text */
328 repl = range_String(collect_String(renderBlockChars_Text(
329 &fontFiraSansRegular_Embedded,
330 11, /* should be larger if shaded */
331 quadrants_TextBlockMode,
332 &(iString){ iBlockLiteral(
333 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) })));
334 }
335 else if (startsWith_Rangecc(name, "ST:")) { /* shaded text */
336 repl = range_String(collect_String(renderBlockChars_Text(
337 &fontSymbola_Embedded,
338 20,
339 shading_TextBlockMode,
340 &(iString){ iBlockLiteral(
341 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) })));
342 }
343 else if (equal_Rangecc(name, "ALT")) {
344#if defined (iPlatformApple)
345 repl = range_CStr("\u2325");
346#else
347 repl = range_CStr("Alt");
348#endif
349 }
350 else if (equal_Rangecc(name, "ALT+")) {
351#if defined (iPlatformApple)
352 repl = range_CStr("\u2325");
353#else
354 repl = range_CStr("Alt+");
355#endif
356 }
357 else if (equal_Rangecc(name, "CTRL")) {
358#if defined (iPlatformApple)
359 repl = range_CStr("\u2318");
360#else
361 repl = range_CStr("Ctrl");
362#endif
292 } 363 }
364 else if (equal_Rangecc(name, "CTRL+")) {
365#if defined (iPlatformApple)
366 repl = range_CStr("\u2318");
367#else
368 repl = range_CStr("Ctrl+");
369#endif
370 }
371 else if (equal_Rangecc(name, "SHIFT")) {
372#if defined (iPlatformApple)
373 repl = range_CStr("\u21e7");
374#else
375 repl = range_CStr("Shift");
376#endif
377 }
378 else if (equal_Rangecc(name, "SHIFT+")) {
379#if defined (iPlatformApple)
380 repl = range_CStr("\u21e7");
381#else
382 repl = range_CStr("Shift+");
383#endif
384 }
385 remove_Block(replaced, span.start, size_Range(&span));
386 insertData_Block(replaced, span.start, repl.start, size_Range(&repl));
387 iZap(m);
293 } while (matchRange_RegExp(var, range_Block(replaced), &m)); 388 } while (matchRange_RegExp(var, range_Block(replaced), &m));
294 block = replaced; 389 block = replaced;
295 } 390 }
@@ -305,9 +400,12 @@ void submit_GmRequest(iGmRequest *d) {
305 clear_GmResponse(&d->resp); 400 clear_GmResponse(&d->resp);
306 iUrl url; 401 iUrl url;
307 init_Url(&url, &d->url); 402 init_Url(&url, &d->url);
308 /* Check for special protocols. */ 403 /* Check for special schemes. */
309 /* TODO: If this were a library, these could be handled via callbacks. */ 404 /* TODO: If this were a library, these could be handled via callbacks. */
310 if (equalCase_Rangecc(&url.protocol, "about")) { 405 /* TODO: Handle app's configured proxies and these via the same mechanism. */
406 const iString *host = collect_String(newRange_String(url.host));
407 uint16_t port = toInt_String(collect_String(newRange_String(url.port)));
408 if (equalCase_Rangecc(url.scheme, "about")) {
311 const iBlock *src = aboutPageSource_(url.path); 409 const iBlock *src = aboutPageSource_(url.path);
312 if (src) { 410 if (src) {
313 d->resp.statusCode = success_GmStatusCode; 411 d->resp.statusCode = success_GmStatusCode;
@@ -323,7 +421,7 @@ void submit_GmRequest(iGmRequest *d) {
323 iNotifyAudience(d, finished, GmRequestFinished); 421 iNotifyAudience(d, finished, GmRequestFinished);
324 return; 422 return;
325 } 423 }
326 else if (equalCase_Rangecc(&url.protocol, "file")) { 424 else if (equalCase_Rangecc(url.scheme, "file")) {
327 iString *path = collect_String(urlDecode_String(collect_String(newRange_String(url.path)))); 425 iString *path = collect_String(urlDecode_String(collect_String(newRange_String(url.path))));
328 iFile * f = new_File(path); 426 iFile * f = new_File(path);
329 if (open_File(f, readOnly_FileMode)) { 427 if (open_File(f, readOnly_FileMode)) {
@@ -361,9 +459,9 @@ void submit_GmRequest(iGmRequest *d) {
361 iNotifyAudience(d, finished, GmRequestFinished); 459 iNotifyAudience(d, finished, GmRequestFinished);
362 return; 460 return;
363 } 461 }
364 else if (equalCase_Rangecc(&url.protocol, "data")) { 462 else if (equalCase_Rangecc(url.scheme, "data")) {
365 d->resp.statusCode = success_GmStatusCode; 463 d->resp.statusCode = success_GmStatusCode;
366 iString *src = collectNewCStr_String(url.protocol.start + 5); 464 iString *src = collectNewCStr_String(url.scheme.start + 5);
367 iRangecc header = { constBegin_String(src), constBegin_String(src) }; 465 iRangecc header = { constBegin_String(src), constBegin_String(src) };
368 while (header.end < constEnd_String(src) && *header.end != ',') { 466 while (header.end < constEnd_String(src) && *header.end != ',') {
369 header.end++; 467 header.end++;
@@ -372,8 +470,8 @@ void submit_GmRequest(iGmRequest *d) {
372 setRange_String(&d->resp.meta, header); 470 setRange_String(&d->resp.meta, header);
373 /* Check what's in the header. */ { 471 /* Check what's in the header. */ {
374 iRangecc entry = iNullRange; 472 iRangecc entry = iNullRange;
375 while (nextSplit_Rangecc(&header, ";", &entry)) { 473 while (nextSplit_Rangecc(header, ";", &entry)) {
376 if (equal_Rangecc(&entry, "base64")) { 474 if (equal_Rangecc(entry, "base64")) {
377 isBase64 = iTrue; 475 isBase64 = iTrue;
378 } 476 }
379 } 477 }
@@ -392,15 +490,31 @@ void submit_GmRequest(iGmRequest *d) {
392 iNotifyAudience(d, finished, GmRequestFinished); 490 iNotifyAudience(d, finished, GmRequestFinished);
393 return; 491 return;
394 } 492 }
493 else if (schemeProxy_App(url.scheme)) {
494 /* User has configured a proxy server for this scheme. */
495 const iString *proxy = schemeProxy_App(url.scheme);
496 if (contains_String(proxy, ':')) {
497 const size_t cpos = indexOf_String(proxy, ':');
498 port = atoi(cstr_String(proxy) + cpos + 1);
499 host = collect_String(newCStrN_String(cstr_String(proxy), cpos));
500 }
501 else {
502 host = proxy;
503 port = 0;
504 }
505 }
395 d->state = receivingHeader_GmRequestState; 506 d->state = receivingHeader_GmRequestState;
396 d->req = new_TlsRequest(); 507 d->req = new_TlsRequest();
508 const iGmIdentity *identity = identityForUrl_GmCerts(d->certs, &d->url);
509 if (identity) {
510 setCertificate_TlsRequest(d->req, identity->cert);
511 }
397 iConnect(TlsRequest, d->req, readyRead, d, readIncoming_GmRequest_); 512 iConnect(TlsRequest, d->req, readyRead, d, readIncoming_GmRequest_);
398 iConnect(TlsRequest, d->req, finished, d, requestFinished_GmRequest_); 513 iConnect(TlsRequest, d->req, finished, d, requestFinished_GmRequest_);
399 uint16_t port = toInt_String(collect_String(newRange_String(url.port)));
400 if (port == 0) { 514 if (port == 0) {
401 port = 1965; /* default Gemini port */ 515 port = 1965; /* default Gemini port */
402 } 516 }
403 setUrl_TlsRequest(d->req, collect_String(newRange_String(url.host)), port); 517 setUrl_TlsRequest(d->req, host, port);
404 setContent_TlsRequest(d->req, 518 setContent_TlsRequest(d->req,
405 utf8_String(collectNewFormat_String("%s\r\n", cstr_String(&d->url)))); 519 utf8_String(collectNewFormat_String("%s\r\n", cstr_String(&d->url))));
406 submit_TlsRequest(d->req); 520 submit_TlsRequest(d->req);