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 <string.h>
8 #include "macro.h"
9 #include "strlcpy.h"
10 #include "strnstr.h"
11 #include "error.h"
12 #include "image.h"
13 #include "storage.h"
14 #define CONFIG_INTERNAL
15 #include "config.h"
16
17 #define DEFAULT_SEARCH_URL "gemini://geminispace.info/search?%s"
18 #define CONFIG_FILE "config.conf"
19
20 struct config config = {0};
21
22 void config_default() {
23 memset(&config, 0, sizeof(config));
24 config.certificateLifespan = 3600 * 24 * 365 * 2; /* 2 years */
25 config.certificateBits = 2048;
26 config.enableHexViewer = 1;
27 config.enableSandbox = 1;
28 config.enableImage = 1;
29 config.enableHistory = 1;
30 config.enableXdg = 1;
31 config.maximumBodyLength = 8388608;
32 config.maximumDisplayLength = 1048576;
33 config.imageParserScratchPad = 10485760;
34 config.maximumRedirects = 5;
35 config.maximumCachedPages= 15;
36 config.maximumHistorySize = 3000;
37 STRLCPY(config.searchEngineURL, DEFAULT_SEARCH_URL);
38 }
39
40 static int set_field(struct field field, int v, char *str) {
41 unsigned int i = 0;
42 for (i = 0; i < LENGTH(fields); i++) {
43 if (STRCMP(fields[i].name, field.name)) continue;
44 if (fields[i].type != field.type) break;
45 switch (fields[i].type) {
46 case VALUE_INT:
47 {
48 int *integer = fields[i].ptr;
49 *integer = v;
50 }
51 break;
52 case VALUE_STRING:
53 strlcpy(fields[i].ptr, str, CONFIG_STRING_LENGTH);
54 break;
55 }
56 break;
57 }
58 return 0;
59 }
60
61 int config_set_field(int id, const char *value) {
62 if (id < 0 || (unsigned)id > LENGTH(fields))
63 return ERROR_INVALID_ARGUMENT;
64 switch (fields[id].type) {
65 case VALUE_INT:
66 {
67 int ivalue = atoi(value);
68 if (!ivalue && strcmp(value, "0"))
69 return ERROR_INVALID_ARGUMENT;
70 *(int*)fields[id].ptr = ivalue;
71 }
72 break;
73 case VALUE_STRING:
74 strlcpy(fields[id].ptr, value, CONFIG_STRING_LENGTH);
75 break;
76 default:
77 return ERROR_INVALID_ARGUMENT;
78 }
79 config_correction();
80 return 0;
81 }
82
83 int config_load() {
84 FILE *f;
85 struct field field;
86 char buf[1024];
87 int i, in_str, in_comment;
88 config_default();
89 if (!(f = storage_fopen(CONFIG_FILE, "r")))
90 return ERROR_STORAGE_ACCESS;
91
92 in_comment = in_str = i = 0;
93 while (1) {
94 int ch = fgetc(f);
95 if (ch == EOF || ch == '\n') {
96 int ivalue;
97 buf[i] = 0;
98 in_comment = in_str = i = 0;
99 ivalue = atoi(buf);
100 if (ivalue || !STRCMP(buf, "0")) {
101 field.type = VALUE_INT;
102 set_field(field, ivalue, NULL);
103 } else {
104 field.type = VALUE_STRING;
105 set_field(field, 0, buf);
106 }
107 field.name[0] = 0;
108 if (ch == EOF) break;
109 continue;
110 }
111 if (in_comment) continue;
112 if (!in_str && ch <= ' ') continue;
113 if (ch == '"') {
114 in_str = !in_str;
115 continue;
116 }
117 if (!in_str && ch == '#') {
118 in_comment = 1;
119 }
120 if (!in_str && ch == '=') {
121 buf[i] = 0;
122 STRLCPY(field.name, buf);
123 i = 0;
124 continue;
125 }
126 if ((unsigned)i >= sizeof(buf)) {
127 i = 0;
128 continue;
129 }
130 buf[i++] = ch;
131 }
132
133 fclose(f);
134 config_correction();
135 return 0;
136 }
137
138 int config_save() {
139
140 FILE *f;
141 unsigned int i;
142 if (!(f = storage_fopen(CONFIG_FILE, "w")))
143 return ERROR_STORAGE_ACCESS;
144 for (i = 0; i < LENGTH(fields); i++) {
145 switch (fields[i].type) {
146 case VALUE_INT:
147 fprintf(f, "%s = %d\n", fields[i].name,
148 *(int*)fields[i].ptr);
149 break;
150 case VALUE_STRING:
151 fprintf(f, "%s = \"%s\"\n", fields[i].name,
152 (char*)fields[i].ptr);
153 break;
154 }
155 }
156
157 fclose(f);
158 return 0;
159 }
160
161 int config_correction() {
162 size_t i;
163 for (i = 0; i < LENGTH(fields); i++) {
164 if (!STRCMP(fields[i].name, "request.maxdisplay")) {
165 unsigned int *value = fields[i].ptr;
166 if (*value < 4096) *value = 4096;
167 }
168 if (!STRCMP(fields[i].name, "search.url")) {
169 char *value = fields[i].ptr;
170 char *ptr = strnstr(value, "%s", CONFIG_STRING_LENGTH);
171 char *second = NULL;
172 if (ptr) {
173 second = strnstr(ptr + 1, "%s",
174 CONFIG_STRING_LENGTH - (ptr - value));
175 }
176 if (!ptr || second) {
177 strlcpy(fields[i].ptr, DEFAULT_SEARCH_URL,
178 CONFIG_STRING_LENGTH);
179 }
180 }
181 }
182 return 0;
183 }
184