0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4 #include <stdlib.h>
5 #include <string.h>
6 #include <stdint.h>
7 #include <unistd.h>
8 #include "strlcpy.h"
9 #include "strnstr.h"
10 #include "macro.h"
11 #include "error.h"
12 #include "sandbox.h"
13 #include "gemini.h"
14 #include "page.h"
15 #include "request.h"
16 #define PARSER_INTERNAL
17 #include "parser.h"
18
19 /* fetch status code and metadata */
20 int parse_response(int fd, size_t length, char *meta, size_t len, int *code,
21 int *bytes_read) {
22
23 char *ptr;
24 char buf[MAX_URL];
25 size_t i;
26 int found;
27
28 found = 0;
29 for (i = 0; i < sizeof(buf) && i < len && i < length; i++) {
30 int ret = read(fd, &buf[i], 1);
31 if (ret != 1) return -1;
32 if (i && buf[i] == '\n' && buf[i - 1] == '\r') {
33 found = 1;
34 break;
35 }
36 }
37 if (!found || i < 1) return -1;
38 *bytes_read = i + 1;
39 buf[i - 1] = '\0';
40
41 ptr = strchr(buf, ' ');
42 if (!ptr) return ERROR_INVALID_METADATA;
43 *ptr = 0;
44 i = atoi(buf);
45 if (!i) return ERROR_INVALID_STATUS;
46
47 memset(meta, 0, len);
48 strlcpy(meta, ptr + 1, len);
49
50 *code = gemini_status_code(i);
51 return 0;
52 }
53
54 void parser_request(int in, int out) {
55 parser_sandbox(out, "vgmi [request]");
56 while (1) { /* TODO: send error code */
57
58 int ret, bytes;
59 size_t length;
60 struct request request = {0};
61
62 if (vread(in, &length, sizeof(length))) break;
63 ret = parse_response(in, length, V(request.meta),
64 &request.status, &bytes);
65 if (request.status == -1) ret = ERROR_INVALID_STATUS;
66 if (ret) {
67 uint8_t byte;
68 request.status = -1;
69 for (; bytes < (signed)length; bytes++)
70 read(in, P(byte));
71 write(out, P(request.status));
72 write(out, P(ret));
73 continue;
74 }
75
76 write(out, P(request.status));
77 if (request.status == GMI_SUCCESS && !request.meta[0]) {
78 STRLCPY(request.meta, "text/gemini");
79 }
80 write(out, V(request.meta));
81 if (request.status == GMI_SUCCESS) {
82 request.page.mime = parser_mime(V(request.meta));
83 request.page.offset = bytes;
84 } else {
85 request.page.mime = 0;
86 request.page.offset = 0;
87 }
88 write(out, P(request.page.mime));
89 write(out, P(request.page.offset));
90
91 if (request.status == GMI_SUCCESS &&
92 is_gemtext(V(request.meta))) {
93 if (parse_links(in, length - bytes, out)) {
94 break;
95 }
96 } else {
97 uint8_t byte;
98 size_t i;
99 for (i = bytes; i < length; i++) read(in, &byte, 1);
100 }
101 }
102 }
103