"notsuffix" realm flag was recognized in raddb/realms but not in radiusd.conf
[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((char *)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                 if ((cf_section_value_find(cs, "notsuffix")) != NULL)
640                         c->notsuffix = 1;
641
642
643                 c->next = realms;
644                 realms = c;
645
646         }
647
648         return 0;
649 }
650
651 /* JLN
652  * Create the linked list of realms from the new configuration type
653  * This way we don't have to change to much in the other source-files
654  */
655
656 static int generate_clients(const char *filename)
657 {
658         CONF_SECTION    *cs;
659         RADCLIENT       *c;
660         char            *hostnm, *secret, *shortnm;
661
662         for (cs = cf_subsection_find_next(config, NULL, "client")
663              ; cs ;
664              cs = cf_subsection_find_next(config, cs, "client")) {
665                 if (!cs->name2) {
666                         radlog(L_CONS|L_ERR, "%s[%d]: Missing client name", filename, cs->item.lineno);
667                         return -1;
668                 }
669                 /*
670                  * Check the lengths, we don't want any core dumps
671                  */
672                 hostnm = cs->name2;
673                 secret = cf_section_value_find(cs, "secret");
674                 shortnm = cf_section_value_find(cs, "shortname");
675
676                 if (strlen(secret) >= sizeof(c->secret)) {
677                         radlog(L_ERR, "%s[%d]: Secret of length %d is greater than the allowed maximum of %d.",
678                             filename, cs->item.lineno,
679                             strlen(secret), sizeof(c->secret) - 1);
680                         return -1;
681                 }
682                 if (strlen(shortnm) > sizeof(c->shortname)) {
683                         radlog(L_ERR, "%s[%d]: NAS short name of length %d is greater than the allowed maximum of %d.",
684                             filename, cs->item.lineno,
685                             strlen(shortnm), sizeof(c->shortname) - 1);
686                         return -1;
687                 }
688                 /*
689                  * The size is fine.. Let's create the buffer
690                  */
691                 if ((c = malloc(sizeof(RADCLIENT))) == NULL) {
692                         radlog(L_CONS|L_ERR, "Out of memory");
693                         return -1;
694                 }
695
696                 c->ipaddr = ip_getaddr(hostnm);
697                 strcpy((char *)c->secret, secret);
698                 strcpy(c->shortname, shortnm);
699                 ip_hostname(c->longname, sizeof(c->longname),
700                             c->ipaddr);
701
702                 c->next = clients;
703                 clients = c;
704         }
705
706         return 0;
707 }
708
709 /* 
710  * Return a CONF_PAIR within a CONF_SECTION.
711  */
712
713 CONF_PAIR *cf_pair_find(CONF_SECTION *section, const char *name)
714 {
715         CONF_ITEM       *ci;
716
717         if (section == NULL) {
718           section = config;
719         }
720
721         for (ci = section->children; ci; ci = ci->next) {
722                 if (ci->type != CONF_ITEM_PAIR)
723                         continue;
724                 if (name == NULL || strcmp(cf_itemtopair(ci)->attr, name) == 0)
725                         break;
726         }
727
728         return cf_itemtopair(ci);
729 }
730
731 /*
732  * Return the attr of a CONF_PAIR
733  */
734
735 char *cf_pair_attr(CONF_PAIR *pair)
736 {
737         return (pair ? pair->attr : NULL);
738 }
739
740 /*
741  * Return the value of a CONF_PAIR
742  */
743
744 char *cf_pair_value(CONF_PAIR *pair)
745 {
746         return (pair ? pair->value : NULL);
747 }
748
749 /*
750  * Return the first label of a CONF_SECTION
751  */
752
753 char *cf_section_name1(CONF_SECTION *section)
754 {
755         return (section ? section->name1 : NULL);
756 }
757
758 /*
759  * Return the second label of a CONF_SECTION
760  */
761
762 char *cf_section_name2(CONF_SECTION *section)
763 {
764         return (section ? section->name2 : NULL);
765 }
766
767 /* 
768  * Find a value in a CONF_SECTION
769  */
770 char *cf_section_value_find(CONF_SECTION *section, const char *attr)
771 {
772         CONF_PAIR       *cp;
773
774         cp = cf_pair_find(section, attr);
775
776         return (cp ? cp->value : NULL);
777 }
778
779 /*
780  * Return the next pair after a CONF_PAIR
781  * with a certain name (char *attr) If the requested
782  * attr is NULL, any attr matches.
783  */
784
785 CONF_PAIR *cf_pair_find_next(CONF_SECTION *section, CONF_PAIR *pair, const char *attr)
786 {
787         CONF_ITEM       *ci;
788
789         /*
790          * If pair is NULL this must be a first time run
791          * Find the pair with correct name
792          */
793
794         if (pair == NULL){
795                 return cf_pair_find(section, attr);
796         }
797
798         ci = cf_pairtoitem(pair)->next;
799
800         for (; ci; ci = ci->next) {
801                 if (ci->type != CONF_ITEM_PAIR)
802                         continue;
803                 if (attr == NULL || strcmp(cf_itemtopair(ci)->attr, attr) == 0)
804                         break;
805         }
806
807         return cf_itemtopair(ci);
808 }
809
810 /*
811  * Find a CONF_SECTION, or return the root if name is NULL
812  */
813
814 CONF_SECTION *cf_section_find(const char *name)
815 {
816         if (name)
817                 return cf_section_sub_find(config, name);
818         else
819                 return config;
820 }
821
822 /*
823  * Find a sub-section in a section
824  */
825
826 CONF_SECTION *cf_section_sub_find(CONF_SECTION *section, const char *name)
827 {
828
829         CONF_ITEM *ci;
830         for (ci = section->children; ci; ci = ci->next) {
831                 if (ci->type != CONF_ITEM_SECTION)
832                         continue;
833                 if (strcmp(cf_itemtosection(ci)->name1, name) == 0)
834                         break;
835         }
836
837         return cf_itemtosection(ci);
838
839 }
840
841 /*
842  * Return the next subsection after a CONF_SECTION
843  * with a certain name1 (char *name1). If the requested
844  * name1 is NULL, any name1 matches.
845  */
846
847 CONF_SECTION *cf_subsection_find_next(CONF_SECTION *section,
848                                       CONF_SECTION *subsection,
849                                       const char *name1)
850 {
851         CONF_ITEM       *ci;
852
853         /*
854          * If subsection is NULL this must be a first time run
855          * Find the subsection with correct name
856          */
857
858         if (subsection == NULL){
859                 ci = section->children;
860         } else {
861                 ci = cf_sectiontoitem(subsection)->next;
862         }
863
864         for (; ci; ci = ci->next) {
865                 if (ci->type != CONF_ITEM_SECTION)
866                         continue;
867                 if (name1 == NULL ||
868                     strcmp(cf_itemtosection(ci)->name1, name1) == 0)
869                         break;
870         }
871
872         return cf_itemtosection(ci);
873 }
874
875 /*
876  * Return the next item after a CONF_ITEM.
877  */
878
879 CONF_ITEM *cf_item_find_next(CONF_SECTION *section, CONF_ITEM *item)
880 {
881         /*
882          * If item is NULL this must be a first time run
883          * Return the first item
884          */
885
886         if (item == NULL) {
887                 return section->children;
888         } else {
889                 return item->next;
890         }
891 }
892
893 int cf_section_lineno(CONF_SECTION *section)
894 {
895         return cf_sectiontoitem(section)->lineno;
896 }
897
898 int cf_pair_lineno(CONF_PAIR *pair)
899 {
900         return cf_pairtoitem(pair)->lineno;
901 }
902
903 int cf_item_is_section(CONF_ITEM *item)
904 {
905         return item->type == CONF_ITEM_SECTION;
906 }
907
908
909 /* 
910  * JMG dump_config tries to dump the config structure in a readable format
911  * 
912 */
913
914 static int dump_config_section(CONF_SECTION *cs, int indent)
915 {
916         CONF_SECTION    *scs;
917         CONF_PAIR       *cp;
918         CONF_ITEM       *ci;
919
920         /* The DEBUG macro doesn't let me
921          *   for(i=0;i<indent;++i) debugputchar('\t');
922          * so I had to get creative. --Pac. */
923
924         for (ci = cs->children; ci; ci = ci->next) {
925                 if (ci->type == CONF_ITEM_PAIR) {
926                         cp=cf_itemtopair(ci);
927                         DEBUG("%.*s%s = %s",
928                                 indent, "\t\t\t\t\t\t\t\t\t\t\t",
929                                 cp->attr, cp->value);
930                 } else {
931                         scs=cf_itemtosection(ci);
932                         DEBUG("%.*s%s %s%s{",
933                                 indent, "\t\t\t\t\t\t\t\t\t\t\t",
934                                 scs->name1,
935                                 scs->name2 ? scs->name2 : "",
936                                 scs->name2 ?  " " : "");
937                         dump_config_section(scs, indent+1);
938                         DEBUG("%.*s}",
939                                 indent, "\t\t\t\t\t\t\t\t\t\t\t");
940                 }
941         }
942
943         return 0;
944 }
945
946 int dump_config(void)
947 {
948         return dump_config_section(config, 0);
949 }