1#include <limits.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <strings.h>
6
7#include "config.h"
8#include "log.h"
9#include "users.h"
10
11static int load_path(const char* path);
12static int parse_bool(const char* s, bool* out);
13static int set_kv(const char* k, const char* v);
14static char* trim(char* s);
15
16/* defaults */
17char media_dir[4096] = "/mnt/sto/Media/";
18char server_addr[64] = "127.0.0.1";
19int server_port = 8088;
20bool verbose_log = true;
21char cors_origin[1024] = "";
22int http_io_timeout = 5;
23int max_clients = 64;
24int auth_delay = 250;
25
26/* Load configuration from a file path
27 Returns 0=Success, -1=Failure */
28static int load_path(const char* path)
29{
30 FILE* f = fopen(path, "r");
31 if (!f)
32 return -1;
33
34 char* line = NULL;
35 size_t cap = 0;
36 ssize_t n;
37
38 while ((n = getline(&line, &cap, f)) >= 0) {
39 (void)n;
40
41 char* s = trim(line);
42 if (s[0] == '\0')
43 continue;
44 if (s[0] == '#')
45 continue;
46
47 char* eq = strchr(s, '=');
48 if (!eq)
49 continue;
50
51 *eq = '\0';
52 char* k = trim(s);
53 char* v = trim(eq + 1);
54
55 if (k[0] == '\0')
56 continue;
57
58 if (set_kv(k, v) < 0)
59 LOG_FIELD(true, "CONF", "Config Error", "%s=%s", k, v);
60 }
61
62 free(line);
63 fclose(f);
64 return 0;
65}
66
67/* Parse bool string value
68 Returns 0=Success, -1=Failure */
69static int parse_bool(const char* s, bool* out)
70{
71 if (!s || !out)
72 return -1;
73
74 if (strcmp(s, "1") == 0 || strcasecmp(s, "true") == 0 || strcasecmp(s, "yes") == 0)
75 *out = true;
76 else if (strcmp(s, "0") == 0 || strcasecmp(s, "false") == 0 || strcasecmp(s, "no") == 0)
77 *out = false;
78 else
79 return -1;
80
81 return 0;
82}
83
84/* Apply a config key/value pair
85 Returns 0=Success, -1=Failure */
86static int set_kv(const char* k, const char* v)
87{
88 /* regular */
89 if (strcmp(k, "media_dir") == 0) {
90 snprintf(media_dir, sizeof(media_dir), "%s", v);
91 return 0;
92 }
93
94 if (strcmp(k, "server_addr") == 0) {
95 snprintf(server_addr, sizeof(server_addr), "%s", v);
96 return 0;
97 }
98
99 if (strcmp(k, "server_port") == 0) {
100 char* end = NULL;
101 long p = strtol(v, &end, 10);
102
103 if (end == v || *end != '\0') {
104 LOG_FIELD(true, "CONF", "Config Error", "%s invalid integer '%s'", k, v);
105 return -1;
106 }
107 if (p < 1 || p > 65535) {
108 LOG_FIELD(true, "CONF", "Config Error", "server_port out of bounds (1..65535)");
109 return -1;
110 }
111
112 server_port = (int)p;
113 return 0;
114 }
115
116 if (strcmp(k, "verbose_log") == 0) {
117 bool b;
118 if (parse_bool(v, &b) < 0){
119 LOG_FIELD(true, "CONF", "Config Error", "verbose_log invalid bool \"%s\"", v);
120 return -1;
121 }
122
123 verbose_log = b;
124 return 0;
125 }
126
127 if (strcmp(k, "cors_origin") == 0) {
128 snprintf(cors_origin, sizeof(cors_origin), "%s", v);
129 return 0;
130 }
131
132 if (strcmp(k, "http_io_timeout") == 0) {
133 char* end = NULL;
134 long t = strtol(v, &end, 10);
135
136 if (end == v || *end != '\0') {
137 LOG_FIELD(true, "CONF", "Config Error", "%s invalid integer '%s'", k, v);
138 return -1;
139 }
140 if (t < 1 || t > 300) {
141 LOG_FIELD(true, "CONF", "Config Error", "http_io_timeout too %s (1..300)", t > 300 ? "high" : "low");
142 return -1;
143 }
144
145 http_io_timeout = (int)t;
146 return 0;
147 }
148
149 if (strcmp(k, "max_clients") == 0) {
150 char* end = NULL;
151 long n = strtol(v, &end, 10);
152
153 if (end == v || *end != '\0') {
154 LOG_FIELD(true, "CONF", "Config Error", "%s invalid integer '%s'", k, v);
155 return -1;
156 }
157 if (n < 1 || n > 1024) {
158 LOG_FIELD(true, "CONF", "Config Error", "max_clients too %s (1..1024)", n > 1024 ? "high" : "low");
159 return -1;
160 }
161
162 max_clients = (int)n;
163 return 0;
164 }
165
166 if (strcmp(k, "auth_delay") == 0) {
167 char* end = NULL;
168 long n = strtol(v, &end, 10);
169
170 if (end == v || *end != '\0') {
171 LOG_FIELD(true, "CONF", "Config Error", "%s invalid integer '%s'", k, v);
172 return -1;
173 }
174 if (n < 0 || n > INT_MAX) {
175 LOG_FIELD(true, "CONF", "Config Error", "auth_delay out of bounds (0..%d)", INT_MAX);
176 return -1;
177 }
178
179 auth_delay = (int)n;
180 return 0;
181 }
182
183 /* auth */
184 if (strcmp(k, "user") == 0) {
185 int r = users_push(v);
186 if (r < 0)
187 LOG_FIELD(true, "CONF", "Config Error", "user invalid '%s'", v);
188 return r;
189 }
190
191 if (strcmp(k, "pass") == 0) {
192 int r = users_set_pass(v);
193 if (r < 0)
194 LOG_FIELD(true, "CONF", "Config Error", "pass invalid '%s'", v);
195 return r;
196 }
197
198 if (strcmp(k, "allow") == 0) {
199 int r = users_add_allow(v);
200 if (r < 0)
201 LOG_FIELD(true, "CONF", "Config Error", "allow invalid '%s'", v);
202 return r;
203 }
204
205 /* unknown key */
206 LOG_FIELD(true, "CONF", "Unknown Key Value", "%s=%s", k, v);
207 return 0;
208}
209
210/* Trim leading and trailing whitespace
211 Returns Ptr to trimmed string */
212static char* trim(char* s)
213{
214 while (*s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'))
215 s++;
216
217 char* e = s + strlen(s);
218 while (e > s) {
219 char c = e[-1];
220 if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
221 break;
222 e--;
223 }
224 *e = '\0';
225 return s;
226}
227
228void config_load(void)
229{
230 const char* e = getenv("PARADOS_CONFIG");
231 const char* path = "";
232 if (e && e[0]) {
233 (void)load_path(e);
234 path = e;
235 goto log;
236 }
237
238 if (load_path("/etc/parados.conf") == 0) {
239 path = "/etc/parados.conf";
240 goto log;
241 }
242
243 if (load_path("./parados.conf") == 0) {
244 path = "./parados.conf";
245 goto log;
246 }
247
248 LOG(true, "CONF", "Unable to load configuration... using defaults");
249 return;
250
251log:
252 (void)0;
253
254 char port[8];
255 char tmo[16];
256 char maxc[16];
257 char adly[16];
258
259 LOG_FIELD(true, "CONF", "Config File", "%s", path);
260 LOG_FIELD(true, "CONF", "Media Directory", "%s", media_dir);
261 LOG_FIELD(true, "CONF", "Server Address", "%s", server_addr);
262 snprintf(port, sizeof(port), "%d", server_port);
263 LOG_FIELD(true, "CONF", "Server Port", "%s", port);
264 LOG_FIELD(true, "CONF", "Verbose Logging", "%s", (verbose_log) ? "true" : "false");
265 LOG_FIELD(true, "CONF", "CORS origins", "%s", cors_origin);
266 snprintf(tmo, sizeof(tmo), "%d", http_io_timeout);
267 LOG_FIELD(true, "CONF", "HTTP IO Timeout", "%s", tmo);
268 snprintf(maxc, sizeof(maxc), "%d", max_clients);
269 LOG_FIELD(true, "CONF", "Max Clients", "%s", maxc);
270 snprintf(adly, sizeof(adly), "%d", auth_delay);
271 LOG_FIELD(true, "CONF", "Auth Delay", "%s (ms)", adly);
272
273 return;
274}
275