27df21de044ac4161e3603e0925f13a63607b15f
[freeradius.git] / src / main / conffile.c
1 /*
2  * conffile.c   Read the radiusd.conf file.
3  *
4  *              Yep I should learn to use lex & yacc, or at least
5  *              write a decent parser. I know how to do that, really :)
6  *              miquels@cistron.nl
7  *
8  * Version:     $Id$
9  *
10  */
11
12 #include "autoconf.h"
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <assert.h>
17
18 #include "radiusd.h"
19 #include "conffile.h"
20 #include "token.h"
21 #include "modules.h"
22
23 static const char rcsid[] =
24 "$Id$";
25
26 #define xalloc malloc
27 #define xstrdup strdup
28
29 typedef enum conf_type {
30         CONF_ITEM_PAIR,
31         CONF_ITEM_SECTION
32 } CONF_ITEM_TYPE;
33
34 struct conf_item {
35         struct conf_item        *next;
36         struct conf_part        *parent;
37         int                     lineno;
38         CONF_ITEM_TYPE          type;
39 };
40 struct conf_pair {
41         CONF_ITEM       item;
42         char            *attr;
43         char            *value;
44         int             operator;
45 };
46 struct conf_part {
47         CONF_ITEM               item;
48         char                    *name1;
49         char                    *name2;
50         struct conf_item        *children;
51 };
52
53 CONF_SECTION    *config = NULL;
54
55 extern RADCLIENT *clients;
56 extern REALM     *realms;
57
58 static int generate_realms(const char *filename);
59 static int generate_clients(const char *filename);
60
61 #ifndef RADIUS_CONFIG
62 #define RADIUS_CONFIG "radiusd.conf"
63 #endif
64
65 /*
66  *      Isolate the scary casts in these tiny provably-safe functions
67  */
68 CONF_PAIR *cf_itemtopair(CONF_ITEM *ci)
69 {
70         if (ci == NULL)
71                 return NULL;
72         assert(ci->type == CONF_ITEM_PAIR);
73         return (CONF_PAIR *)ci;
74 }
75 CONF_SECTION *cf_itemtosection(CONF_ITEM *ci)
76 {
77         if (ci == NULL)
78                 return NULL;
79         assert(ci->type == CONF_ITEM_SECTION);
80         return (CONF_SECTION *)ci;
81 }
82 static CONF_ITEM *cf_pairtoitem(CONF_PAIR *cp)
83 {
84         if (cp == NULL)
85                 return NULL;
86         return (CONF_ITEM *)cp;
87 }
88 static CONF_ITEM *cf_sectiontoitem(CONF_SECTION *cs)
89 {
90         if (cs == NULL)
91                 return NULL;
92         return (CONF_ITEM *)cs;
93 }
94
95 /*
96  *      Create a new CONF_PAIR
97  */
98 static CONF_PAIR *cf_pair_alloc(const char *attr, const char *value,
99                                 int operator, CONF_SECTION *parent)
100 {
101         CONF_PAIR       *cp;
102
103         cp = (CONF_PAIR *)xalloc(sizeof(CONF_PAIR));
104         memset(cp, 0, sizeof(CONF_PAIR));
105         cp->item.type = CONF_ITEM_PAIR;
106         cp->item.parent = parent;
107         cp->attr = xstrdup(attr);
108         cp->value = xstrdup(value);
109         cp->operator = operator;
110
111         return cp;
112 }
113
114 /*
115  *      Add an item to a configuration section.
116  */
117 static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci_new)
118 {
119         CONF_ITEM *ci;
120         
121         for (ci = cs->children; ci && ci->next; ci = ci->next)
122                 ;
123
124         if (ci == NULL)
125                 cs->children = ci_new;
126         else
127                 ci->next = ci_new;
128 }
129
130 /*
131  *      Free a CONF_PAIR
132  */
133 void cf_pair_free(CONF_PAIR *cp)
134 {
135         if (cp == NULL) return;
136
137         if (cp->attr)  free(cp->attr);
138         if (cp->value) free(cp->value);
139         free(cp);
140 }
141
142 /*
143  *      Allocate a CONF_SECTION
144  */
145 static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
146                                       CONF_SECTION *parent)
147 {
148         CONF_SECTION    *cs;
149
150         if (name1 == NULL || !name1[0]) name1 = "main";
151
152         cs = (CONF_SECTION *)xalloc(sizeof(CONF_SECTION));
153         memset(cs, 0, sizeof(CONF_SECTION));
154         cs->item.type = CONF_ITEM_SECTION;
155         cs->item.parent = parent;
156         cs->name1 = xstrdup(name1);
157         cs->name2 = (name2 && *name2) ? xstrdup(name2) : NULL;
158
159         return cs;
160 }
161
162 /*
163  *      Free a CONF_SECTION
164  */
165 void cf_section_free(CONF_SECTION *cs)
166 {
167         CONF_ITEM       *ci, *next;
168
169         if (cs == NULL) return;
170
171         for (ci = cs->children; ci; ci = next) {
172                 next = ci->next;
173                 if (ci->type==CONF_ITEM_PAIR)
174                         cf_pair_free(cf_itemtopair(ci));
175                 else
176                         cf_section_free(cf_itemtosection(ci));
177         }
178
179         if (cs->name1) free(cs->name1);
180         if (cs->name2) free(cs->name2);
181
182         /*
183          * And free the section
184          */
185         free(cs);
186 }
187
188 /*
189  *      Parse a configuration section into user-supplied variables.
190  */
191 int cf_section_parse(CONF_SECTION *cs, const CONF_PARSER *variables)
192 {
193         int             i;
194         char            **q;
195         CONF_PAIR       *cp;
196         uint32_t        ipaddr;
197         char            buffer[1024];
198         const char      *value;
199
200         /*
201          *      Handle the user-supplied variables.
202          */
203         for (i = 0; variables[i].name != NULL; i++) {
204                 value = variables[i].dflt;
205
206                 cp = cf_pair_find(cs, variables[i].name);
207                 if (cp) {
208                         value = cp->value;
209                 }
210                 
211                 switch (variables[i].type)
212                 {
213                 case PW_TYPE_BOOLEAN:
214                         /*
215                          *      Allow yes/no and on/off
216                          */
217                         if ((strcasecmp(value, "yes") == 0) ||
218                             (strcasecmp(value, "on") == 0)) {
219                                 *(int *)variables[i].data = 1;
220                         } else if ((strcasecmp(value, "no") == 0) ||
221                                    (strcasecmp(value, "off") == 0)) {
222                                 *(int *)variables[i].data = 0;
223                         } else {
224                                 *(int *)variables[i].data = 0;
225                                 radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, variables[i].name);
226                                 return -1;
227                         }
228                         DEBUG2("Config: %s.%s = %s",
229                                cs->name1,
230                                variables[i].name,
231                                value);
232                         break;
233
234                 case PW_TYPE_INTEGER:
235                         *(int *)variables[i].data = strtol(value, 0, 0);
236                         DEBUG2("Config: %s.%s = %d",
237                                cs->name1,
238                                variables[i].name,
239                                *(int *)variables[i].data);
240                         break;
241                         
242                 case PW_TYPE_STRING_PTR:
243                         q = (char **) variables[i].data;
244                         if (*q != NULL) {
245                                 free(*q);
246                         }
247                         DEBUG2("Config: %s.%s = \"%s\"",
248                                cs->name1,
249                                variables[i].name,
250                                value ? value : "(null)");
251                         *q = value ? strdup(value) : NULL;
252                         break;
253
254                 case PW_TYPE_IPADDR:
255                         /*
256                          *      Allow '*' as any address
257                          */
258                         if (strcmp(value, "*") == 0) {
259                                 *(uint32_t *) variables[i].data = 0;
260                                 break;
261                         }
262                         ipaddr = ip_getaddr(value);
263                         if (ipaddr == 0) {
264                                 radlog(L_ERR, "Can't find IP address for host %s", value);
265                                 return -1;
266                         }
267                         DEBUG2("Config: %s.%s = %s IP address [%s]",
268                                cs->name1,
269                                variables[i].name,
270                                value, ip_ntoa(buffer, ipaddr));
271                         *(uint32_t *) variables[i].data = ipaddr;
272                         break;
273                         
274                 default:
275                         radlog(L_ERR, "type %d not supported yet", variables[i].type);
276                         return -1;
277                         break;
278                 } /* switch over variable type */
279         } /* for all variables in the configuration section */
280         
281         return 0;
282 }
283
284
285 /*
286  *      Read a part of the config file.
287  */
288 static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
289                                      const char *name1, const char *name2,
290                                      CONF_SECTION *parent)
291 {
292         CONF_SECTION    *cs, *css, *outercs;
293         CONF_PAIR       *cpn;
294         char            *ptr, *p, *q;
295         char            buf[8192];
296         char            buf1[1024];
297         char            buf2[1024];
298         char            buf3[1024];
299         int             t1, t2, t3;
300         
301         /*
302          *      Ensure that the user can't add CONF_SECTIONs
303          *      with 'internal' names;
304          */
305         if ((name1 != NULL) && (name1[0] == '_')) {
306                 radlog(L_ERR, "%s[%d]: Illegal configuration section name",
307                     cf, *lineno);
308                 return NULL;
309         }
310
311         /*
312          *      Allow for $INCLUDE files???
313          *
314          *      This sure looks wrong. But it looked wrong before I touched
315          *      the file, so don't blame me. Please, just pass the config
316          *      file through cpp or m4 and cut out the bloat. --Pac.
317          */
318         if (name1 && strcasecmp(name1, "$INCLUDE") == 0) {
319           return conf_read(name2);
320         }
321
322         /*
323          *      Allocate new section.
324          */
325         cs = cf_section_alloc(name1, name2, parent);
326         cs->item.lineno = *lineno;
327
328         /*
329          *      Read.
330          */
331         while (fgets(buf, sizeof(buf), fp) != NULL) {
332                 (*lineno)++;
333                 ptr = buf;
334
335                 if (*ptr == '#')
336                         continue;
337
338                 /*
339                  *      No '=': must be a section or sub-section.
340                  */
341                 if (strchr(ptr, '=') == NULL) {
342                         t1 = gettoken(&ptr, buf1, sizeof(buf1));
343                         t2 = gettoken(&ptr, buf2, sizeof(buf2));
344                         t3 = gettoken(&ptr, buf3, sizeof(buf3));
345                 } else {
346                         t1 = gettoken(&ptr, buf1, sizeof(buf1));
347                         t2 = gettoken(&ptr, buf2, sizeof(buf2));
348                         t3 = getword(&ptr, buf3, sizeof(buf3));
349                 }
350
351                 if (buf1[0] == 0 || buf1[0] == '#')
352                         continue;
353
354                 /*
355                  *      See if it's the end of a section.
356                  */
357                 if (t1 == T_RCBRACE) {
358                         if (name1 == NULL || buf2[0]) {
359                                 radlog(L_ERR, "%s[%d]: Unexpected end of section",
360                                         cf, *lineno);
361                                 cf_section_free(cs);
362                                 return NULL;
363                         }
364                         return cs;
365                 }
366
367                 /*
368                  * Perhaps a subsection.
369                  */
370                 if (t2 == T_LCBRACE || t3 == T_LCBRACE) {
371                         css = cf_section_read(cf, lineno, fp, buf1,
372                                               t2==T_LCBRACE ? NULL : buf2, cs);
373                         if (css == NULL) {
374                                 cf_section_free(cs);
375                                 return NULL;
376                         }
377                         cf_item_add(cs, cf_sectiontoitem(css));
378
379                         continue;               
380                 }
381
382                 /*
383                  *      Ignore semi-colons.
384                  */
385                 if (*buf2 == ';') *buf2 = '\0';
386
387                 /*
388                  *      Must be a normal attr = value line.
389                  */
390                 if (buf1[0] != 0 && buf2[0] == 0 && buf3[0] == 0) {
391                         t2 = T_OP_EQ;
392                 } else if (buf1[0] == 0 || buf2[0] == 0 || buf3[0] == 0 ||
393                           (t2 < T_EQSTART || t2 > T_EQEND)) {
394                         radlog(L_ERR, "%s[%d]: Line is not in 'attribute = value' format",
395                                 cf, *lineno);
396                         cf_section_free(cs);
397                         return NULL;
398                 }
399
400                 /*
401                  *      Ensure that the user can't add CONF_PAIRs
402                  *      with 'internal' names;
403                  */
404                 if (buf1[0] == '_') {
405                         radlog(L_ERR, "%s[%d]: Illegal configuration pair name \"%s\"",
406                                 cf, *lineno, buf1);
407                         cf_section_free(cs);
408                         return NULL;
409                 }
410                 
411                 /*
412                  *      Handle variable substitution via ${foo}
413                  */
414                 p = buf;
415                 ptr = buf3;
416                 while (*ptr >= ' ') {
417                         /*
418                          *      Ignore anything other than "${"
419                          */
420                         if ((*ptr != '$') ||
421                             (ptr[1] != '{')) {
422                                 *(p++) = *(ptr++);
423                                 continue;
424                         }
425
426                         /*
427                          *      Look for trailing '}', and silently
428                          *      ignore anything that doesn't match.
429                          */
430                         q = strchr(ptr, '}');
431                         if (q == NULL) {
432                                 *(p++) = *(ptr++);
433                                 continue;
434                         }
435                         
436                         memcpy(buf2, ptr + 2, q - ptr - 2);
437                         buf2[q - ptr - 2] = '\0';
438                         cpn = cf_pair_find(cs, buf2);
439                         /* Also look recursively up the section tree,
440                          * so things like ${confdir} can be defined
441                          * there and used inside the module config
442                          * sections */
443                         for (outercs=cs->item.parent
444                              ; !cpn && outercs ;
445                              outercs=outercs->item.parent) {
446                                 cpn = cf_pair_find(outercs, buf2);
447                         }
448                         if (!cpn) {
449                                 radlog(L_ERR, "%s[%d]: Unknown variable \"%s\"",
450                                     cf, *lineno, buf2);
451                                 cf_section_free(cs);
452                                 return NULL;
453                         }
454                         strcpy(p, cpn->value);
455                         p += strlen(p);
456                         ptr = q + 1;
457                 }
458                 *p = '\0';
459
460                 /*
461                  *      Add this CONF_PAIR to our CONF_SECTION
462                  */
463                 cpn = cf_pair_alloc(buf1, buf, t2, parent);
464                 cpn->item.lineno = *lineno;
465                 cf_item_add(cs, cf_pairtoitem(cpn));
466         }
467
468         /*
469          *      See if EOF was unexpected ..
470          */
471         if (name1 != NULL) {
472                 radlog(L_ERR, "%s[%d]: unexpected end of file", cf, *lineno);
473                 cf_section_free(cs);
474                 return NULL;
475         }
476
477         return cs;
478 }
479
480 /*
481  *      Read the config file.
482  */
483 CONF_SECTION *conf_read(const char *conffile)
484 {
485         FILE            *fp;
486         int             lineno = 0;
487         CONF_SECTION    *cs;
488         
489         if ((fp = fopen(conffile, "r")) == NULL) {
490                 radlog(L_ERR, "cannot open %s: %s",
491                         conffile, strerror(errno));
492                 return NULL;
493         }
494
495         cs = cf_section_read(conffile, &lineno, fp, NULL, NULL, NULL);
496         fclose(fp);
497
498         return cs;
499 }
500
501 /* JLN
502  * Read the configuration and library
503  * This uses the new kind of configuration file as defined by
504  * Miquel at http://www.miquels.cistron.nl/radius/
505  */
506
507 int read_radius_conf_file(void)
508 {
509         char buffer[256];
510         CONF_SECTION *cs;
511
512         /* Lets go for the new configuration files */
513
514         sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
515         if ((cs = conf_read(buffer)) == NULL) {
516                 return -1;
517         }
518
519         /*
520          *      Free the old configuration data, and replace it
521          *      with the new one.
522          */
523         cf_section_free(config);
524         config = cs;
525         
526
527
528         /*
529          * Fail if we can't generate list of clients
530          */
531
532         if (generate_clients(buffer) < 0) {
533                 return -1;
534         }
535
536         /*
537          * If there isn't any realms it isn't fatal..
538          */
539         if (generate_realms(buffer) < 0) {
540                 return -1;
541         }
542
543         return 0;       
544 }
545
546 /* JLN
547  * Create the linked list of realms from the new configuration type
548  * This way we don't have to change to much in the other source-files
549  */
550
551 static int generate_realms(const char *filename)
552 {
553         CONF_SECTION    *cs;
554         REALM           *c;
555         char            *s, *authhost, *accthost;
556
557         for (cs = cf_subsection_find_next(config, NULL, "realm")
558              ; cs ;
559              cs = cf_subsection_find_next(config, cs, "realm")) {
560                 if (!cs->name2) {
561                         radlog(L_CONS|L_ERR, "%s[%d]: Missing realm name", filename, cs->item.lineno);
562                         return -1;
563                 }
564                 /*
565                  * We've found a realm, allocate space for it
566                  */
567                 if ((c = malloc(sizeof(REALM))) == NULL) {
568                         radlog(L_CONS|L_ERR, "Out of memory");
569                         return -1;
570                 }
571                 memset(c, 0, sizeof(REALM));
572                 /*
573                  * An authhost must exist in the configuration
574                  */
575                 if ((authhost = cf_section_value_find(cs, "authhost")) == NULL) {
576                         radlog(L_CONS|L_ERR, 
577                             "%s[%d]: No authhost entry in realm", 
578                             filename, cs->item.lineno);
579                         return -1;
580                 }
581                 if ((s = strchr(authhost, ':')) != NULL) {
582                         *s++ = 0;
583                         c->auth_port = atoi(s);
584                 } else {
585                         c->auth_port = auth_port;
586                 }
587                 accthost = cf_section_value_find(cs, "accthost");
588                 if ((s =strchr(accthost, ':')) != NULL) {
589                         *s++ = 0;
590                         c->acct_port = atoi(s); 
591                 } else {
592                         c->acct_port = acct_port;
593                 }
594                 if (strcmp(authhost, "LOCAL") != 0)
595                         c->ipaddr = ip_getaddr(authhost);
596
597                 /* 
598                  * Double check length, just to be sure!
599                  */
600                 if (strlen(authhost) >= sizeof(c->server)) {
601                         radlog(L_ERR, "%s[%d]: Server name of length %d is greater that allowed: %d",
602                             filename, cs->item.lineno,
603                             strlen(authhost), sizeof(c->server) - 1);
604                         return -1;
605                 }
606                 if (strlen(cs->name2) >= sizeof(c->realm)) {
607                         radlog(L_ERR, "%s[%d]: Realm name of length %d is greater than allowed %d",
608                             filename, cs->item.lineno,
609                             strlen(cs->name2), sizeof(c->server) - 1);
610                         return -1;
611                 }
612                 
613                 strcpy(c->realm, cs->name2);
614                 strcpy(c->server, authhost);    
615
616                 s = cf_section_value_find(cs, "secret");
617                 if (s == NULL) {
618                         radlog(L_ERR, "%s[%d]: No shared secret supplied for realm",
619                             filename, cs->item.lineno);
620                         return -1;
621                 }
622
623                 if (strlen(s) >= sizeof(c->secret)) {
624                   radlog(L_ERR, "%s[%d]: Secret of length %d is greater than the allowed maximum of %d.",
625                       filename, cs->item.lineno,
626                       strlen(s), sizeof(c->secret) - 1);
627                   return -1;
628                 }
629                 strNcpy(c->secret, s, sizeof(c->secret));
630
631                 c->striprealm = 1;
632                 
633                 if ((cf_section_value_find(cs, "nostrip")) != NULL)
634                         c->striprealm = 0;
635                 if ((cf_section_value_find(cs, "noacct")) != NULL)
636                         c->acct_port = 0;
637                 if ((cf_section_value_find(cs, "trusted")) != NULL)
638                         c->trusted = 1;
639
640                 c->next = realms;
641                 realms = c;
642
643         }
644
645         return 0;
646 }
647
648 /* JLN
649  * Create the linked list of realms from the new configuration type
650  * This way we don't have to change to much in the other source-files
651  */
652
653 static int generate_clients(const char *filename)
654 {
655         CONF_SECTION    *cs;
656         RADCLIENT       *c;
657         char            *hostnm, *secret, *shortnm;
658
659         for (cs = cf_subsection_find_next(config, NULL, "client")
660              ; cs ;
661              cs = cf_subsection_find_next(config, cs, "client")) {
662                 if (!cs->name2) {
663                         radlog(L_CONS|L_ERR, "%s[%d]: Missing client name", filename, cs->item.lineno);
664                         return -1;
665                 }
666                 /*
667                  * Check the lengths, we don't want any core dumps
668                  */
669                 hostnm = cs->name2;
670                 secret = cf_section_value_find(cs, "secret");
671                 shortnm = cf_section_value_find(cs, "shortname");
672
673                 if (strlen(secret) >= sizeof(c->secret)) {
674                         radlog(L_ERR, "%s[%d]: Secret of length %d is greater than the allowed maximum of %d.",
675                             filename, cs->item.lineno,
676                             strlen(secret), sizeof(c->secret) - 1);
677                         return -1;
678                 }
679                 if (strlen(shortnm) > sizeof(c->shortname)) {
680                         radlog(L_ERR, "%s[%d]: NAS short name of length %d is greater than the allowed maximum of %d.",
681                             filename, cs->item.lineno,
682                             strlen(shortnm), sizeof(c->shortname) - 1);
683                         return -1;
684                 }
685                 /*
686                  * The size is fine.. Let's create the buffer
687                  */
688                 if ((c = malloc(sizeof(RADCLIENT))) == NULL) {
689                         radlog(L_CONS|L_ERR, "Out of memory");
690                         return -1;
691                 }
692
693                 c->ipaddr = ip_getaddr(hostnm);
694                 strcpy(c->secret, secret);
695                 strcpy(c->shortname, shortnm);
696                 ip_hostname(c->longname, sizeof(c->longname),
697                             c->ipaddr);
698
699                 c->next = clients;
700                 clients = c;
701         }
702
703         return 0;
704 }
705
706 /* 
707  * Return a CONF_PAIR within a CONF_SECTION.
708  */
709
710 CONF_PAIR *cf_pair_find(CONF_SECTION *section, const char *name)
711 {
712         CONF_ITEM       *ci;
713
714         if (section == NULL) {
715           section = config;
716         }
717
718         for (ci = section->children; ci; ci = ci->next) {
719                 if (ci->type != CONF_ITEM_PAIR)
720                         continue;
721                 if (name == NULL || strcmp(cf_itemtopair(ci)->attr, name) == 0)
722                         break;
723         }
724
725         return cf_itemtopair(ci);
726 }
727
728 /*
729  * Return the attr of a CONF_PAIR
730  */
731
732 char *cf_pair_attr(CONF_PAIR *pair)
733 {
734         return (pair ? pair->attr : NULL);
735 }
736
737 /*
738  * Return the value of a CONF_PAIR
739  */
740
741 char *cf_pair_value(CONF_PAIR *pair)
742 {
743         return (pair ? pair->value : NULL);
744 }
745
746 /*
747  * Return the first label of a CONF_SECTION
748  */
749
750 char *cf_section_name1(CONF_SECTION *section)
751 {
752         return (section ? section->name1 : NULL);
753 }
754
755 /*
756  * Return the second label of a CONF_SECTION
757  */
758
759 char *cf_section_name2(CONF_SECTION *section)
760 {
761         return (section ? section->name2 : NULL);
762 }
763
764 /* 
765  * Find a value in a CONF_SECTION
766  */
767 char *cf_section_value_find(CONF_SECTION *section, const char *attr)
768 {
769         CONF_PAIR       *cp;
770
771         cp = cf_pair_find(section, attr);
772
773         return (cp ? cp->value : NULL);
774 }
775
776 /*
777  * Return the next pair after a CONF_PAIR
778  * with a certain name (char *attr) If the requested
779  * attr is NULL, any attr matches.
780  */
781
782 CONF_PAIR *cf_pair_find_next(CONF_SECTION *section, CONF_PAIR *pair, const char *attr)
783 {
784         CONF_ITEM       *ci;
785
786         /*
787          * If pair is NULL this must be a first time run
788          * Find the pair with correct name
789          */
790
791         if (pair == NULL){
792                 return cf_pair_find(section, attr);
793         }
794
795         ci = cf_pairtoitem(pair)->next;
796
797         for (; ci; ci = ci->next) {
798                 if (ci->type != CONF_ITEM_PAIR)
799                         continue;
800                 if (attr == NULL || strcmp(cf_itemtopair(ci)->attr, attr) == 0)
801                         break;
802         }
803
804         return cf_itemtopair(ci);
805 }
806
807 /*
808  * Find a CONF_SECTION, or return the root if name is NULL
809  */
810
811 CONF_SECTION *cf_section_find(const char *name)
812 {
813         if (name)
814                 return cf_section_sub_find(config, name);
815         else
816                 return config;
817 }
818
819 /*
820  * Find a sub-section in a section
821  */
822
823 CONF_SECTION *cf_section_sub_find(CONF_SECTION *section, const char *name)
824 {
825
826         CONF_ITEM *ci;
827         for (ci = section->children; ci; ci = ci->next) {
828                 if (ci->type != CONF_ITEM_SECTION)
829                         continue;
830                 if (strcmp(cf_itemtosection(ci)->name1, name) == 0)
831                         break;
832         }
833
834         return cf_itemtosection(ci);
835
836 }
837
838 /*
839  * Return the next subsection after a CONF_SECTION
840  * with a certain name1 (char *name1). If the requested
841  * name1 is NULL, any name1 matches.
842  */
843
844 CONF_SECTION *cf_subsection_find_next(CONF_SECTION *section,
845                                       CONF_SECTION *subsection,
846                                       const char *name1)
847 {
848         CONF_ITEM       *ci;
849
850         /*
851          * If subsection is NULL this must be a first time run
852          * Find the subsection with correct name
853          */
854
855         if (subsection == NULL){
856                 ci = section->children;
857         } else {
858                 ci = cf_sectiontoitem(subsection)->next;
859         }
860
861         for (; ci; ci = ci->next) {
862                 if (ci->type != CONF_ITEM_SECTION)
863                         continue;
864                 if (name1 == NULL ||
865                     strcmp(cf_itemtosection(ci)->name1, name1) == 0)
866                         break;
867         }
868
869         return cf_itemtosection(ci);
870 }
871
872 /*
873  * Return the next item after a CONF_ITEM.
874  */
875
876 CONF_ITEM *cf_item_find_next(CONF_SECTION *section, CONF_ITEM *item)
877 {
878         /*
879          * If item is NULL this must be a first time run
880          * Return the first item
881          */
882
883         if (item == NULL) {
884                 return section->children;
885         } else {
886                 return item->next;
887         }
888 }
889
890 int cf_section_lineno(CONF_SECTION *section)
891 {
892         return cf_sectiontoitem(section)->lineno;
893 }
894
895 int cf_pair_lineno(CONF_PAIR *pair)
896 {
897         return cf_pairtoitem(pair)->lineno;
898 }
899
900 int cf_item_is_section(CONF_ITEM *item)
901 {
902         return item->type == CONF_ITEM_SECTION;
903 }
904
905
906 /* 
907  * JMG dump_config tries to dump the config structure in a readable format
908  * 
909 */
910
911 static int dump_config_section(CONF_SECTION *cs, int indent)
912 {
913         CONF_SECTION    *scs;
914         CONF_PAIR       *cp;
915         CONF_ITEM       *ci;
916
917         /* The DEBUG macro doesn't let me
918          *   for(i=0;i<indent;++i) debugputchar('\t');
919          * so I had to get creative. --Pac. */
920
921         for (ci = cs->children; ci; ci = ci->next) {
922                 if (ci->type == CONF_ITEM_PAIR) {
923                         cp=cf_itemtopair(ci);
924                         DEBUG("%.*s%s = %s",
925                                 indent, "\t\t\t\t\t\t\t\t\t\t\t",
926                                 cp->attr, cp->value);
927                 } else {
928                         scs=cf_itemtosection(ci);
929                         DEBUG("%.*s%s %s%s{",
930                                 indent, "\t\t\t\t\t\t\t\t\t\t\t",
931                                 scs->name1,
932                                 scs->name2 ? scs->name2 : "",
933                                 scs->name2 ?  " " : "");
934                         dump_config_section(scs, indent+1);
935                         DEBUG("%.*s}",
936                                 indent, "\t\t\t\t\t\t\t\t\t\t\t");
937                 }
938         }
939
940         return 0;
941 }
942
943 int dump_config(void)
944 {
945         return dump_config_section(config, 0);
946 }