1 /*
2 * Copyright (C) 2002-2003 Fhg Fokus
3 *
4 * This file is part of SEMS, a free SIP media server.
5 *
6 * SEMS is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. This program is released under
10 * the GPL with the additional exemption that compiling, linking,
11 * and/or using OpenSSL is allowed.
12 *
13 * For a license to use the SEMS software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
16 * info@iptel.org
17 *
18 * SEMS is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27 #include "AmConfigReader.h"
28 #include "AmConfig.h"
29 #include "log.h"
30 #include "AmUtils.h"
31 #include "md5.h"
32
33 #include <errno.h>
34 #include <fstream>
35
36 #define IS_SPACE(c) ((c == ' ') || (c == '\t'))
37
38 #define IS_EOL(c) ((c == '\0')||(c == '#'))
39
40 #define TRIM(s) \
41 do{ \
42 while( IS_SPACE(*s) ) s++; \
43 }while(false)
44
fifo_get_line(FILE * fifo_stream,char * str,size_t len)45 static int fifo_get_line(FILE* fifo_stream, char* str, size_t len)
46 {
47 char c;
48 size_t l;
49 char* s=str;
50
51 if(!len)
52 return 0;
53
54 l=len;
55
56 while( l && (c=fgetc(fifo_stream)) && !ferror(fifo_stream) && c!=EOF && c!='\n' ){
57 if(c!='\r'){
58 *(s++) = c;
59 l--;
60 }
61 }
62
63
64 if(l>0){
65 // We need one more character
66 // for trailing '\0'.
67 *s='\0';
68
69 return int(s-str);
70 }
71 else
72 // buffer overran.
73 return -1;
74 }
75
76
loadFile(const string & path)77 int AmConfigReader::loadFile(const string& path)
78 {
79 FILE* fp = fopen(path.c_str(),"r");
80 if(!fp){
81 WARN("could not open configuration file '%s': %s\n",
82 path.c_str(),strerror(errno));
83 return -1;
84 }
85
86 int lc = 0;
87 int ls = 0;
88 char lb[MAX_CONFIG_LINE] = {'\0'};
89
90 char *c,*key_beg,*key_end,*val_beg,*val_end,*inc_beg,*inc_end;
91
92 c=key_beg=key_end=val_beg=val_end=inc_beg=inc_end=0;
93 while(!feof(fp) && ((ls = fifo_get_line(fp, lb, MAX_CONFIG_LINE)) != -1)){
94
95 c=key_beg=key_end=val_beg=val_end=0;
96 lc++;
97
98 c = lb;
99 TRIM(c);
100
101 if(IS_EOL(*c)) continue;
102
103 if (*c == '@') { /* process included config file */
104 c++;
105 TRIM(c);
106 inc_beg = c++;
107 while( !IS_EOL(*c) && !IS_SPACE(*c) ) c++;
108 inc_end = c;
109 string fname = string(inc_beg,inc_end-inc_beg);
110 if (fname.length() && fname[0] != '/')
111 fname = AmConfig::ModConfigPath + fname;
112 if(loadFile(fname))
113 goto error;
114 continue;
115 }
116
117 key_beg = c;
118 while( (*c != '=') && !IS_SPACE(*c) ) c++;
119
120 key_end = c;
121 if(IS_SPACE(*c))
122 TRIM(c);
123 else if( !(c - key_beg) )
124 goto syntax_error;
125
126 if(*c != '=')
127 goto syntax_error;
128
129 c++;
130 TRIM(c);
131
132 if(*c == '"'){
133 char last_c = ' ';
134 val_beg = ++c;
135
136 while( ((*c != '"') || (last_c == '\\')) && (*c != '\0') ) {
137 last_c = *c;
138 c++;
139 }
140
141 if(*c == '\0')
142 goto syntax_error;
143
144 val_end = c;
145 }
146 else {
147 val_beg = c;
148
149 while( !IS_EOL(*c) && !IS_SPACE(*c) ) c++;
150
151 val_end = c;
152 }
153
154 if((key_beg < key_end) && (val_beg <= val_end)) {
155 string keyname = string(key_beg,key_end-key_beg);
156 string val = string(val_beg,val_end-val_beg);
157 if (hasParameter(keyname)) {
158 WARN("while loading '%s': overwriting configuration "
159 "'%s' value '%s' with '%s'\n",
160 path.c_str(), keyname.c_str(),
161 getParameter(keyname).c_str(), val.c_str());
162 }
163
164 keys[keyname] = val;
165
166 // small hack to make include work with right path
167 if (keyname == "plugin_config_path")
168 AmConfig::ModConfigPath = val;
169
170 } else
171 goto syntax_error;
172 }
173
174 fclose(fp);
175 return 0;
176
177 syntax_error:
178 ERROR("syntax error line %i in %s\n",lc,path.c_str());
179 error:
180 fclose(fp);
181 return -1;
182 }
183
loadPluginConf(const string & mod_name)184 int AmConfigReader::loadPluginConf(const string& mod_name)
185 {
186 return loadFile(add2path(AmConfig::ModConfigPath,1,
187 string(mod_name + CONFIG_FILE_SUFFIX).c_str()));
188 }
189
str_get_line(const char ** c,const char * end,char * line_buf,size_t line_buf_len)190 static int str_get_line(const char** c, const char* end, char* line_buf, size_t line_buf_len)
191 {
192 enum {
193 SGL_LINE=0,
194 SGL_COMMENT,
195 SGL_CR,
196 SGL_LF
197 };
198
199 char* out = line_buf;
200 int st = SGL_LINE;
201
202 while((*c < end) && line_buf_len && (st == SGL_LINE)){
203
204 switch(**c) {
205 case '\r': st = SGL_CR; break;
206 case '\n': st = SGL_LF; break;
207 case '#': st = SGL_COMMENT; break;
208
209 default:
210 *out = **c;
211 out++;
212 line_buf_len--;
213 break;
214 }
215
216 (*c)++;
217 }
218
219 if(st == SGL_COMMENT) {
220 while((*c < end) && (st == SGL_COMMENT)){
221
222 switch(*((*c)++)) {
223 case '\r': st = SGL_CR; break;
224 case '\n': st = SGL_LF; break;
225 default: break;
226 }
227
228 (*c)++;
229 }
230 }
231
232 if(st == SGL_CR) {
233 if(**c == '\n') {
234 st = SGL_LF;
235 (*c)++;
236 }
237 else {
238 DBG("strange line ending with CR only\n");
239 }
240 }
241
242 if(line_buf_len > 0){
243 // We need one more character
244 // for trailing '\0'.
245 *out='\0';
246
247 return int(out-line_buf);
248 }
249
250 // buffer overran.
251 return -1;
252 }
253
loadString(const char * cfg_lines,size_t cfg_len)254 int AmConfigReader::loadString(const char* cfg_lines, size_t cfg_len)
255 {
256 int lc = 0;
257 int ls = 0;
258 char lb[MAX_CONFIG_LINE] = {'\0'};
259
260 char *c,*key_beg,*key_end,*val_beg,*val_end,*inc_beg,*inc_end;
261
262 const char* cursor = cfg_lines;
263 const char* cfg_end = cursor + cfg_len;
264
265 c=key_beg=key_end=val_beg=val_end=inc_beg=inc_end=0;
266 while((cursor < cfg_end) &&
267 ((ls = str_get_line(&cursor, cfg_end, lb, MAX_CONFIG_LINE)) != -1)){
268
269 c=key_beg=key_end=val_beg=val_end=0;
270 lc++;
271
272 c = lb;
273 TRIM(c);
274
275 if(IS_EOL(*c)) continue;
276
277 key_beg = c;
278 while( (*c != '=') && !IS_SPACE(*c) ) c++;
279
280 key_end = c;
281 if(IS_SPACE(*c))
282 TRIM(c);
283 else if( !(c - key_beg) )
284 goto syntax_error;
285
286 if(*c != '=')
287 goto syntax_error;
288
289 c++;
290 TRIM(c);
291
292 if(*c == '"'){
293 char last_c = ' ';
294 val_beg = ++c;
295
296 while( ((*c != '"') || (last_c == '\\')) && (*c != '\0') ) {
297 last_c = *c;
298 c++;
299 }
300
301 if(*c == '\0')
302 goto syntax_error;
303
304 val_end = c;
305 }
306 else {
307 val_beg = c;
308
309 while( !IS_EOL(*c) && !IS_SPACE(*c) ) c++;
310
311 val_end = c;
312 }
313
314 if((key_beg < key_end) && (val_beg <= val_end)) {
315 string keyname = string(key_beg,key_end-key_beg);
316 string val = string(val_beg,val_end-val_beg);
317 if (hasParameter(keyname)) {
318 WARN("while loading string: overwriting configuration "
319 "'%s' value '%s' with '%s'\n",
320 keyname.c_str(), getParameter(keyname).c_str(),
321 val.c_str());
322 }
323
324 keys[keyname] = val;
325 } else
326 goto syntax_error;
327 }
328
329 return 0;
330
331 syntax_error:
332 ERROR("syntax error line %i\n",lc);
333 return -1;
334 }
335
getMD5(const string & path,string & md5hash,bool lowercase)336 bool AmConfigReader::getMD5(const string& path, string& md5hash, bool lowercase) {
337 std::ifstream data_file(path.c_str(), std::ios::in | std::ios::binary);
338 if (!data_file) {
339 DBG("could not read file '%s'\n", path.c_str());
340 return false;
341 }
342 // that one is clever...
343 // (see http://www.gamedev.net/community/forums/topic.asp?topic_id=353162 )
344 string file_data((std::istreambuf_iterator<char>(data_file)),
345 std::istreambuf_iterator<char>());
346
347 if (file_data.empty()) {
348 return false;
349 }
350
351 MD5_CTX md5ctx;
352 MD5Init(&md5ctx);
353 MD5Update(&md5ctx, (unsigned char*)file_data.c_str(), file_data.length());
354 unsigned char _md5hash[16];
355 MD5Final(_md5hash, &md5ctx);
356 md5hash = "";
357 for (size_t i=0;i<16;i++) {
358 md5hash+=char2hex(_md5hash[i], lowercase);
359 }
360 return true;
361 }
362
setParameter(const string & param,const string & val)363 void AmConfigReader::setParameter(const string& param, const string& val) {
364 keys[param] = val;
365 }
366
eraseParameter(const string & param)367 void AmConfigReader::eraseParameter(const string& param) {
368 keys.erase(param);
369 }
370
hasParameter(const string & param) const371 bool AmConfigReader::hasParameter(const string& param) const
372 {
373 return (keys.find(param) != keys.end());
374 }
375
376 string __empty_string("");
377
getParameter(const string & param) const378 const string& AmConfigReader::getParameter(const string& param) const
379 {
380 return getParameter(param,__empty_string);
381 }
382
getParameter(const string & param,const string & defval) const383 const string& AmConfigReader::getParameter(const string& param, const string& defval) const
384 {
385 map<string,string>::const_iterator it = keys.find(param);
386 if(it == keys.end())
387 return defval;
388 else
389 return it->second;
390 }
391
getParameterInt(const string & param,unsigned int defval) const392 unsigned int AmConfigReader::getParameterInt(const string& param, unsigned int defval) const
393 {
394 unsigned int result=0;
395 if(!hasParameter(param) || str2i(getParameter(param),result))
396 return defval;
397 else
398 return result;
399 }
400
dump()401 void AmConfigReader::dump()
402 {
403 for(map<string,string>::iterator it = keys.begin();
404 it != keys.end(); it++) {
405
406 DBG("\t%s = %s",it->first.c_str(),it->second.c_str());
407 }
408 }
409