1 //
2 // Configuration.cc
3 //
4 // Configuration: This class provides an object lookup table.  Each object
5 //                in the Configuration is indexed with a string.  The objects
6 //                can be returned by mentioning their string index. Values may
7 //                include files with `/path/to/file` or other configuration
8 //                variables with ${variable}
9 //
10 // Part of the ht://Dig package   <http://www.htdig.org/>
11 // Copyright (c) 1999-2004 The ht://Dig Group
12 // For copyright details, see the file COPYING in your distribution
13 // or the GNU Library General Public License (LGPL) version 2 or later
14 // <http://www.gnu.org/copyleft/lgpl.html>
15 //
16 // $Id: Configuration.cc,v 1.20 2004/05/28 13:15:20 lha Exp $
17 //
18 
19 #ifdef HAVE_CONFIG_H
20 #include "htconfig.h"
21 #endif /* HAVE_CONFIG_H */
22 
23 #include <stdio.h>
24 #include "Configuration.h"
25 #include "htString.h"
26 #include "ParsedString.h"
27 
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <locale.h>
31 
32 
33 //*********************************************************************
34 // Configuration::Configuration()
35 //
Configuration()36 Configuration::Configuration() : separators("=:"), allow_multiple(0)
37 {
38 }
39 
40 
41 //*********************************************************************
42 // void Configuration::NameValueSeparators(char *s)
43 //
NameValueSeparators(const String & s)44 void Configuration::NameValueSeparators(const String& s)
45 {
46     separators = s;
47 }
48 
49 
50 //*********************************************************************
51 //   Add an entry to the configuration table.
52 //
Add(const String & str_arg)53 void Configuration::Add(const String& str_arg)
54 {
55     const char* str = str_arg;
56     String	name, value;
57 
58     while (str && *str)
59     {
60         while (isspace(*str))
61             str++;
62         name = 0;
63         if (!isalpha(*str))
64             break;
65         // Some isalnum() implementations don't allow all the letters that
66         // isalpha() does, e.g. accented ones.  They're not POSIX.2 compliant
67         // but we won't punish them with an infinite loop...
68         if (!isalnum(*str))
69             break;
70         while (isalnum(*str) || *str == '-' || *str == '_')
71             name << *str++;
72 
73         name.lowercase();
74 
75         //
76         // We have the name.  Let's see if we will get a value
77         //
78         while (isspace(*str))
79             str++;
80         if (!*str)
81         {
82             //
83             // End of string.  We need to store the name as a boolean TRUE
84             //
85             Add(name, "true");
86             return;
87         }
88 
89         if (!strchr((char*)separators, *str))
90         {
91             //
92             // We are now at a new name.  The previous one needs to be set
93             // to boolean TRUE
94             //
95             Add(name, "true");
96             continue;
97         }
98 
99         //
100         // We now need to deal with the value
101         //
102         str++;			// Skip the separator
103         while (isspace(*str))
104             str++;
105         if (!*str)
106         {
107             //
108             // End of string reached.  The value must be blank
109             //
110             Add(name, "");
111             break;
112         }
113         value = 0;
114         if (*str == '"')
115         {
116             //
117             // Ah!  A quoted value.  This should be easy to deal with...
118             // (Just kidding!)
119             //
120             str++;
121             while (*str && *str != '"')
122             {
123                 value << *str++;
124             }
125             Add(name, value);
126             if (*str == '"')
127                 str++;
128             continue;
129         }
130         else if (*str == '\'')
131         {
132             // A single quoted value.
133             str++;
134             while (*str && *str != '\'')
135             {
136                 value << *str++;
137             }
138             Add(name, value);
139             if (*str == '\'')
140                 str++;
141             continue;
142         }
143         else
144         {
145             //
146             // A non-quoted string.  This string will terminate at the
147             // next blank
148             //
149             while (*str && !isspace(*str))
150             {
151                 value << *str++;
152             }
153             Add(name, value);
154             continue;
155         }
156     }
157 }
158 
159 
160 //*********************************************************************
161 //   Add an entry to the configuration table, without allowing variable
162 //   or file expansion of the value.
163 //
Add(const String & name,const String & value)164 void Configuration::Add(const String& name, const String& value)
165 {
166     String	escaped;
167     const char	*s = value.get();
168     while (*s)
169     {
170         if (strchr("$`\\", *s))
171             escaped << '\\';
172         escaped << *s++;
173     }
174     ParsedString	*ps = new ParsedString(escaped);
175     dcGlobalVars.Add(name, ps);
176 }
177 
178 
179 //*********************************************************************
180 //   Add an entry to the configuration table, allowing parsing for variable
181 //   or file expansion of the value.
182 //
AddParsed(const String & name,const String & value)183 void Configuration::AddParsed(const String& name, const String& value)
184 {
185     ParsedString	*ps = new ParsedString(value);
186     if (mystrcasecmp(name, "locale") == 0)
187     {
188         String str(setlocale(LC_ALL, ps->get(dcGlobalVars)));
189         ps->set(str);
190 
191         //
192         // Set time format to standard to avoid sending If-Modified-Since
193         // http headers in native format which http servers can't
194         // understand
195         //
196         setlocale(LC_TIME, "C");
197     }
198     dcGlobalVars.Add(name, ps);
199 }
200 
201 
202 //*********************************************************************
203 //   Remove an entry from both the hash table and from the list of keys.
204 //
Remove(const String & name)205 int Configuration::Remove(const String& name)
206 {
207     return dcGlobalVars.Remove(name);
208 }
209 
210 
211 //*********************************************************************
212 // char *Configuration::Find(const char *name) const
213 //   Retrieve a variable from the configuration database.  This variable
214 //   will be parsed and a new String object will be returned.
215 //
Find(const String & name) const216 const String Configuration::Find(const String& name) const
217 {
218     ParsedString	*ps = (ParsedString *) dcGlobalVars[name];
219     if (ps)
220     {
221         return ps->get(dcGlobalVars);
222     }
223     else
224     {
225 #ifdef DEBUG
226         fprintf (stderr, "Could not find configuration option %s\n", (const char*)name);
227 #endif
228         return 0;
229     }
230 }
231 
232 //-
233 // Return 1 if the value of configuration attribute <b>name</b> has
234 // been set, 0 otherwise
Exists(const String & name) const235 int Configuration::Exists(const String& name) const
236 {
237     return dcGlobalVars.Exists(name);
238 }
239 
240 //*********************************************************************
Get_Object(char * name)241 Object *Configuration::Get_Object(char *name) {
242 return dcGlobalVars[name];
243 }
244 
245 
246 //*********************************************************************
247 //
Value(const String & name,int default_value) const248 int Configuration::Value(const String& name, int default_value) const
249 {
250     return Find(name).as_integer(default_value);
251 }
252 
253 
254 //*********************************************************************
255 //
Double(const String & name,double default_value) const256 double Configuration::Double(const String& name, double default_value) const
257 {
258     return Find(name).as_double(default_value);
259 }
260 
261 
262 //*********************************************************************
263 // int Configuration::Boolean(char *name, int default_value)
264 //
Boolean(const String & name,int default_value) const265 int Configuration::Boolean(const String& name, int default_value) const
266 {
267     int		value = default_value;
268     const String s = Find(name);
269     if (s[0])
270     {
271         if (s.nocase_compare("true") == 0 ||
272             s.nocase_compare("yes") == 0 ||
273             s.nocase_compare("1") == 0)
274             value = 1;
275         else if (s.nocase_compare("false") == 0 ||
276                  s.nocase_compare("no") == 0 ||
277                  s.nocase_compare("0") == 0)
278             value = 0;
279     }
280 
281     return value;
282 }
283 
284 
285 //*********************************************************************
286 //
operator [](const String & name) const287 const String Configuration::operator[](const String& name) const
288 {
289     return Find(name);
290 }
291 
292 
293 //*********************************************************************
294 //
Read(const String & filename)295 int Configuration::Read(const String& filename)
296 {
297     FILE* in = fopen((const char*)filename, "r");
298 
299     if(!in) {
300       fprintf(stderr, "Configuration::Read: cannot open %s for reading : ", (const char*)filename);
301       perror("");
302       return NOTOK;
303     }
304 
305 #define CONFIG_BUFFER_SIZE (50*1024)
306      //
307      // Make the line buffer large so that we can read long lists of start
308      // URLs.
309      //
310      char	buffer[CONFIG_BUFFER_SIZE + 1];
311      char	*current;
312      String	line;
313      String	name;
314      char	*value;
315      int         len;
316      while (fgets(buffer, CONFIG_BUFFER_SIZE, in))
317      {
318          line << buffer;
319          line.chop("\r\n");
320          if (line.last() == '\\')
321          {
322              line.chop(1);
323              continue;			// Append the next line to this one
324          }
325 
326          current = line.get();
327          if (*current == '#' || *current == '\0')
328          {
329              line = 0;
330              continue;			// Comments and blank lines are skipped
331          }
332 
333          name = strtok(current, ": =\t");
334          value = strtok(0, "\r\n");
335          if (!value)
336              value = "";			// Blank value
337 
338          //
339          // Skip any whitespace before the actual text
340          //
341          while (*value == ' ' || *value == '\t')
342              value++;
343  	len = strlen(value) - 1;
344  	//
345  	// Skip any whitespace after the actual text
346  	//
347 	while (len >= 0 && (value[len] == ' ' || value[len] == '\t'))
348  	  {
349  	    value[len] = '\0';
350  	    len--;
351  	  }
352 
353  	if (mystrcasecmp((char*)name, "include") == 0)
354  	{
355  	    ParsedString	ps(value);
356  	    String		str(ps.get(dcGlobalVars));
357  	    if (str[0] != '/')		// Given file name not fully qualified
358  	    {
359  		str = filename;		// so strip dir. name from current one
360  		len = str.lastIndexOf('/') + 1;
361  		if (len > 0)
362  		    str.chop(str.length() - len);
363  		else
364  		    str = "";		// No slash in current filename
365  		str << ps.get(dcGlobalVars);
366  	    }
367  	    Read(str);
368  	    line = 0;
369  	    continue;
370  	}
371 
372          AddParsed(name, value);
373          line = 0;
374      }
375      fclose(in);
376      return OK;
377 }
378 
379 
380 //*********************************************************************
381 // void Configuration::Defaults(ConfigDefaults *array)
382 //
Defaults(const ConfigDefaults * array)383 void Configuration::Defaults(const ConfigDefaults *array)
384 {
385     for (int i = 0; array[i].name; i++)
386     {
387         AddParsed(array[i].name, array[i].value);
388     }
389 }
390 
391