Mention PAM file.
[doldaconnect.git] / daemon / conf.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <langinfo.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <malloc.h>
23#include <string.h>
24#include <sys/types.h>
25#include <errno.h>
26#include <wctype.h>
27#include <stddef.h>
28#include <wchar.h>
29#include <iconv.h>
30#include <arpa/inet.h>
31
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35#include "conf.h"
36#include "log.h"
37#include "utils.h"
38
39static struct configmod *modules = NULL;
40
41#if 0
42static void dumpconfig(void)
43{
44 struct configmod *mod;
45 struct configvar *var;
46
47 for(mod = modules; mod != NULL; mod = mod->next)
48 {
49 printf("%s:\n", mod->name);
50 for(var = mod->vars; var->type != CONF_VAR_END; var++)
51 {
52 switch(var->type)
53 {
54 case CONF_VAR_BOOL:
55 printf("\t%s: %s\n", var->name, var->val.num?"t":"f");
56 break;
57 case CONF_VAR_INT:
58 printf("\t%s: %i\n", var->name, var->val.num);
59 break;
60 case CONF_VAR_STRING:
61 printf("\t%s: \"%ls\" (%i)\n", var->name, var->val.str, wcslen(var->val.str));
62 break;
63 case CONF_VAR_IPV4:
64 printf("\t%s: %s\n", var->name, inet_ntoa(var->val.ipv4));
65 break;
66 }
67 }
68 }
69}
70#endif
71
72struct configvar *confgetvar(char *modname, char *varname)
73{
74 struct configmod *m;
75 struct configvar *v;
76
77 for(m = modules; m != NULL; m = m->next)
78 {
79 if(!strcmp(m->name, modname))
80 {
81 for(v = m->vars; v->type != CONF_VAR_END; v++)
82 {
83 if(!strcmp(v->name, varname))
84 return(v);
85 }
86 break;
87 }
88 }
89 return(NULL);
90}
91
92void confregmod(struct configmod *mod)
93{
94 struct configvar *var;
95
96 for(var = mod->vars; var->type != CONF_VAR_END; var++)
97 {
98 switch(var->type)
99 {
100 case CONF_VAR_BOOL:
101 case CONF_VAR_INT:
102 var->val.num = var->defaults.num;
103 break;
104 case CONF_VAR_STRING:
105 if(var->defaults.str != NULL)
106 {
107 var->val.str = swcsdup(var->defaults.str);
108 } else {
109 var->val.str = NULL;
110 }
111 break;
112 case CONF_VAR_IPV4:
113 var->val.ipv4.s_addr = var->defaults.ipv4.s_addr;
114 break;
115 }
116 CBCHAININIT(var, conf_update);
117 }
118 mod->next = modules;
119 modules = mod;
120}
121
122int runconfcmd(int argc, wchar_t **argv)
123{
124 struct configmod *module;
125 struct configvar *var;
126 struct configcmd *cmd;
127 int ret, handled;
128 wchar_t *p;
129 char *cmdn, *buf, *buf2, *valbuf;
130 long num;
131 struct in_addr newipv4;
132 int cb;
133
134 if(argc < 1)
135 return(0);
136 if((cmdn = icwcstombs(argv[0], "us-ascii")) == NULL)
137 {
138 flog(LOG_WARNING, "could not convert %ls to us-ascii", argv[0]);
139 return(1);
140 }
141 ret = 1;
142 handled = 0;
143 if(!strcmp(cmdn, "set"))
144 {
145 handled = 1;
146 ret = 0;
147 if((p = wcschr(argv[1], L'.')) == NULL)
148 {
149 flog(LOG_WARNING, "illegal configuration variable format: %ls", argv[1]);
150 errno = EINVAL;
151 free(cmdn);
152 return(1);
153 }
154 *(p++) = L'\0';
155 if((buf = icwcstombs(argv[1], "us-ascii")) == NULL)
156 {
157 flog(LOG_WARNING, "could not convert %ls to us-ascii", argv[1]);
158 free(cmdn);
159 return(1);
160 }
161 if((buf2 = icwcstombs(p, "us-ascii")) == NULL)
162 {
163 free(buf);
164 flog(LOG_WARNING, "could not convert %ls to us-ascii", p);
165 free(cmdn);
166 return(1);
167 }
168 for(module = modules; module != NULL; module = module->next)
169 {
170 if(!strcmp(module->name, buf) && (module->vars != NULL))
171 {
172 for(var = module->vars; var->type != CONF_VAR_END; var++)
173 {
174 if(!strcmp(var->name, buf2))
175 {
176 cb = 0;
177 switch(var->type)
178 {
179 case CONF_VAR_BOOL:
180 wcstolower(argv[2]);
181 if(!wcscmp(argv[2], L"off") ||
182 !wcscmp(argv[2], L"false") ||
183 !wcscmp(argv[2], L"no") ||
184 !wcscmp(argv[2], L"0"))
185 {
186 if(var->val.num)
187 cb = 1;
188 var->val.num = 0;
189 } else if(!wcscmp(argv[2], L"on") ||
190 !wcscmp(argv[2], L"true") ||
191 !wcscmp(argv[2], L"yes") ||
192 !wcscmp(argv[2], L"1")) {
193 if(!var->val.num)
194 cb = 1;
195 var->val.num = 1;
196 } else {
197 flog(LOG_WARNING, "unrecognized boolean: %ls", argv[2]);
198 }
199 break;
200 case CONF_VAR_INT:
201 num = wcstol(argv[2], &p, 0);
202 if(p == argv[2])
203 {
204 flog(LOG_WARNING, "%ls: not a number, ignoring", argv[2]);
205 ret = 1;
206 } else {
207 if(*p != L'\0')
208 flog(LOG_WARNING, "%ls: could not entirely parse as a number, ignoring trailing garbage", argv[2]);
209 if(num != var->val.num)
210 cb = 1;
211 var->val.num = num;
212 }
213 break;
214 case CONF_VAR_STRING:
215 if(wcscmp(var->val.str, argv[2]))
216 cb = 1;
217 free(var->val.str);
218 var->val.str = swcsdup(argv[2]);
219 break;
220 case CONF_VAR_IPV4:
221 if((valbuf = icwcstombs(argv[2], "us-ascii")) == NULL)
222 {
223 flog(LOG_WARNING, "could not convert IPv4 address to as-ascii in var %s, ignoring", buf2);
224 } else {
225 if(!inet_aton(valbuf, &newipv4))
226 {
227 flog(LOG_WARNING, "could not parse IPv4 address (%s), ignoring", valbuf);
228 memcpy(&var->val.ipv4, &var->defaults.ipv4, sizeof(var->val.ipv4));
229 } else {
230 if(memcmp(&newipv4, &var->val.ipv4, sizeof(newipv4)))
231 cb = 1;
232 memcpy(&var->val.ipv4, &newipv4, sizeof(newipv4));
233 }
234 free(valbuf);
235 }
236 break;
237 }
238 if(cb)
239 CBCHAINDOCB(var, conf_update, var);
240 break;
241 }
242 }
243 if(var == NULL)
244 flog(LOG_WARNING, "variable %s not found, ignoring set command", buf2);
245 break;
246 }
247 }
248 if(module == NULL)
249 flog(LOG_WARNING, "module %s not found, ignoring set command", buf);
250 free(buf2);
251 free(buf);
252 }
253 for(module = modules; !handled && (module != NULL); module = module->next)
254 {
255 if(module->cmds != NULL)
256 {
257 for(cmd = module->cmds; cmd->name != NULL; cmd++)
258 {
259 if(!strcmp(cmd->name, cmdn))
260 {
261 handled = 1;
262 ret = cmd->handler(argc, argv);
263 break;
264 }
265 }
266 }
267 }
268 if(!handled)
269 flog(LOG_WARNING, "command not found: %s", cmdn);
270 free(cmdn);
271 return(ret);
272}
273
274char *findconfigfile(void)
275{
276 static char pathbuf[128];
277 char *p, *p2;
278
279 if(getenv("HOME") != NULL)
280 {
281 snprintf(pathbuf, sizeof(pathbuf), "%s/.doldacond", getenv("HOME"));
282 if(!access(pathbuf, R_OK))
283 return(pathbuf);
284 }
285 p = CONFIG_PATH;
286 do
287 {
288 p2 = strchr(p, ':');
289 if(p2 != NULL)
290 {
291 memcpy(pathbuf, p, p2 - p);
292 pathbuf[p2 - p] = 0;
293 if(!access(pathbuf, R_OK))
294 return(pathbuf);
295 } else {
296 if(!access(p, R_OK))
297 return(p);
298 }
299 p = p2 + 1;
300 } while(p2 != NULL);
301 return(NULL);
302}
303
304void readconfig(FILE *stream)
305{
306 int state;
307 wint_t c;
308 wchar_t *words[16];
309 wchar_t *buf, *p, *p2;
310 int w;
311 int line;
312
313 buf = smalloc(sizeof(wchar_t) * 1024);
314 state = 0;
315 c = getwc(stream);
316 w = 0;
317 line = 1;
318 p = buf;
319 while(c != WEOF)
320 {
321 if(c == '#')
322 {
323 do
324 c = getwc(stream);
325 while((c != WEOF) && (c != L'\n'));
326 continue;
327 }
328 switch(state)
329 {
330 case 0:
331 if(iswspace(c))
332 {
333 if(c == L'\n')
334 {
335 line++;
336 if(runconfcmd(w, words))
337 flog(LOG_WARNING, "ignoring this command on line %i", line);
338 w = 0;
339 }
340 c = getwc(stream);
341 } else {
342 state = 1;
343 p2 = p;
344 }
345 break;
346 case 1:
347 if(c == L'\"')
348 {
349 state = 2;
350 c = getwc(stream);
351 } else if(iswspace(c)) {
352 if(w >= 16)
353 {
354 flog(LOG_WARNING, "too many words on config line %i, ignoring rest", line);
355 } else {
356 *(p++) = L'\0';
357 words[w++] = p2;
358 }
359 state = 0;
360 } else {
361 if(c == L'\\')
362 c = getwc(stream);
363 if(p - buf < 1023)
364 *(p++) = c;
365 else
366 flog(LOG_WARNING, "too many characters on config line %i, ignoring rest", line);
367 c = getwc(stream);
368 }
369 break;
370 case 2:
371 if(c == L'\"')
372 {
373 c = getwc(stream);
374 state = 1;
375 } else {
376 if(c == L'\\')
377 c = getwc(stream);
378 if(p - buf < 1023)
379 *(p++) = c;
380 else
381 flog(LOG_WARNING, "too many characters on config line %i, ignoring rest", line);
382 c = getwc(stream);
383 }
384 break;
385 }
386 }
387 free(buf);
388 if(ferror(stream))
389 flog(LOG_WARNING, "error on configuration stream: %s", strerror(errno));
390 if(state != 0)
391 flog(LOG_WARNING, "unexpected end of file");
392}