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