Made it 2.0.0, and removed the changes that are in 1.1.x, as
[freeradius.git] / src / modules / rlm_policy / parse.c
1 /*
2  * parse.c              Parse a policy language
3  *
4  * Version:     $Id$
5  *
6  *   This program 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.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2004  Alan DeKok <aland@ox.org>
21  */
22
23 #include "rlm_policy.h"
24
25 #ifdef HAVE_DIRENT_H
26 #include <dirent.h>
27
28 #ifdef HAVE_SYS_STAT_H
29 #include <sys/stat.h>
30 #endif
31 #endif
32
33 #include <freeradius-devel/modules.h>
34
35 const LRAD_NAME_NUMBER policy_return_codes[] = {
36         { "reject", RLM_MODULE_REJECT },
37         { "fail", RLM_MODULE_FAIL },
38         { "ok", RLM_MODULE_OK },
39         { "handled", RLM_MODULE_HANDLED },
40         { "invalid", RLM_MODULE_INVALID },
41         { "userlock", RLM_MODULE_USERLOCK },
42         { "notfound", RLM_MODULE_NOTFOUND },
43         { "noop", RLM_MODULE_NOOP },
44         { "updated", RLM_MODULE_UPDATED },
45         { NULL, RLM_MODULE_NUMCODES }
46 };
47
48 /*
49  *      Explanations of what the lexical tokens are.
50  */
51 static const LRAD_NAME_NUMBER policy_explanations[] = {
52         { "invalid input", POLICY_LEX_BAD },
53         { "end of file", POLICY_LEX_EOF },
54         { "end of line", POLICY_LEX_EOL },
55         { "whitespace", POLICY_LEX_WHITESPACE },
56         { "hash mark", POLICY_LEX_HASH },
57         { "left bracket", POLICY_LEX_L_BRACKET },
58         { "right bracket", POLICY_LEX_R_BRACKET },
59         { "{", POLICY_LEX_LC_BRACKET },
60         { "}", POLICY_LEX_RC_BRACKET },
61         { "comma", POLICY_LEX_COMMA },
62         { "logical AND", POLICY_LEX_L_AND },
63         { "logical OR", POLICY_LEX_L_OR },
64         { "AND", POLICY_LEX_AND },
65         { "OR", POLICY_LEX_OR },
66         { "logical NOT", POLICY_LEX_L_NOT },
67         { "assignment", POLICY_LEX_ASSIGN },
68         { "comparison", POLICY_LEX_CMP_EQUALS },
69         { "comparison", POLICY_LEX_CMP_NOT_EQUALS },
70         { "comparison", POLICY_LEX_LT },
71         { "comparison", POLICY_LEX_GT },
72         { "comparison", POLICY_LEX_LE },
73         { "comparison", POLICY_LEX_GT },
74         { "comparison", POLICY_LEX_RX_EQUALS },
75         { "comparison", POLICY_LEX_RX_NOT_EQUALS },
76         { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
77         { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
78         { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
79         { "bare word", POLICY_LEX_BARE_WORD },
80
81         { NULL, -1 }
82 };
83
84
85 const LRAD_NAME_NUMBER rlm_policy_tokens[] = {
86         { "EOF", POLICY_LEX_EOF },
87         { "#", POLICY_LEX_HASH },
88         { "(", POLICY_LEX_L_BRACKET },
89         { ")", POLICY_LEX_R_BRACKET },
90         { "{", POLICY_LEX_LC_BRACKET },
91         { "}", POLICY_LEX_RC_BRACKET },
92         { ",", POLICY_LEX_COMMA },
93         { "&&", POLICY_LEX_L_AND },
94         { "||", POLICY_LEX_L_OR },
95         { "&", POLICY_LEX_AND },
96         { "|", POLICY_LEX_OR },
97         { "!", POLICY_LEX_L_NOT },
98         { "=", POLICY_LEX_ASSIGN },
99         { "==", POLICY_LEX_CMP_EQUALS },
100         { "!=", POLICY_LEX_CMP_NOT_EQUALS },
101         { "=*", POLICY_LEX_CMP_TRUE },
102         { "!*", POLICY_LEX_CMP_FALSE },
103         { "<", POLICY_LEX_LT },
104         { ">", POLICY_LEX_GT },
105         { "<=", POLICY_LEX_LE },
106         { ">=", POLICY_LEX_GT },
107         { "=~", POLICY_LEX_RX_EQUALS },
108         { "!~", POLICY_LEX_RX_NOT_EQUALS },
109         { ".=", POLICY_LEX_CONCAT_EQUALS },
110         { ":=", POLICY_LEX_SET_EQUALS },
111         { "double quoted string", POLICY_LEX_DOUBLE_QUOTED_STRING },
112         { "single quoted string", POLICY_LEX_SINGLE_QUOTED_STRING },
113         { "back quoted string", POLICY_LEX_BACK_QUOTED_STRING },
114         { "bare word", POLICY_LEX_BARE_WORD },
115
116         { NULL, -1 }
117 };
118
119
120 /*
121  *      Hand-coded lexical analysis of a string.
122  *      Handed input string, updates token, possible a decoded
123  *      string in buffer, and returns the pointer to the next token.
124  *
125  *      Lexical tokens cannot cross a string boundary.
126  */
127 static const char *policy_lex_string(const char *input,
128                                      policy_lex_t *token,
129                                      char *buffer, size_t buflen)
130 {
131         rad_assert(input != NULL);
132
133         if (buffer) *buffer = '\0';
134         
135         switch (*input) {
136         case '\0':
137                 *token = POLICY_LEX_EOL;
138                 return NULL;    /* nothing more to do */
139                 
140         case ' ':
141         case '\t':
142         case '\r':
143         case '\n':
144                 /*
145                  *      Skip over all of the whitespace in one swell foop.
146                  */
147                 *token = POLICY_LEX_WHITESPACE;
148                 while ((*input == ' ') || (*input == '\t') ||
149                        (*input == '\r') || (*input == '\n')) input++;
150                 return input;   /* point to next non-whitespace character */
151                 
152         case '#':               /* ignore everything to the end of the line */
153                 *token = POLICY_LEX_EOL;
154                 return NULL;
155
156         case '(':
157                 *token = POLICY_LEX_L_BRACKET;
158                 return input + 1;
159                 
160         case ')':
161                 *token = POLICY_LEX_R_BRACKET;
162                 return input + 1;
163                 
164         case '{':
165                 *token = POLICY_LEX_LC_BRACKET;
166                 return input + 1;
167                 
168         case '}':
169                 *token = POLICY_LEX_RC_BRACKET;
170                 return input + 1;
171                 
172         case ',':
173                 *token = POLICY_LEX_COMMA;
174                 return input + 1;
175
176         case '+':
177                 switch (input[1]) {
178                 case '=':
179                         *token = POLICY_LEX_PLUS_EQUALS;
180                         input++;
181                         break;
182
183                 default:
184                         *token = POLICY_LEX_PLUS;
185                         break;
186                 }
187                 return input + 1;
188
189         case '-':
190                 switch (input[1]) {
191                 case '=':
192                         *token = POLICY_LEX_MINUS_EQUALS;
193                         input++;
194                         break;
195
196                 default:
197                         *token = POLICY_LEX_MINUS;
198                         break;
199                 }
200                 return input + 1;
201
202         case '.':
203                 if (input[1] == '=') {
204                         *token = POLICY_LEX_CONCAT_EQUALS;
205                         return input + 2;
206                 }
207                 *token = POLICY_LEX_BAD;
208                 return input + 1;
209
210         case ':':
211                 if (input[1] == '=') {
212                         *token = POLICY_LEX_SET_EQUALS;
213                         return input + 2;
214                 }
215                 *token = POLICY_LEX_BAD;
216                 return input + 1;
217
218         case '&':
219                 switch (input[1]) {
220                 case '&':
221                         *token = POLICY_LEX_L_AND;
222                         input++;
223                         break;
224
225                 case '=':
226                         *token = POLICY_LEX_AND_EQUALS;
227                         input++;
228                         break;
229
230                 default:
231                         *token = POLICY_LEX_AND;
232                 }
233                 return input + 1;
234                 
235         case '|':
236                 switch (input[1]) {
237                 case '|':
238                         *token = POLICY_LEX_L_OR;
239                         input++;
240                         break;
241
242                 case '=':
243                         *token = POLICY_LEX_OR_EQUALS;
244                         input++;
245                         break;
246
247                 default:
248                         *token = POLICY_LEX_OR;
249                 }
250                 return input + 1;
251                 
252         case '!':
253                 switch (input[1]) {
254                 case '=':
255                         input++;
256                         *token = POLICY_LEX_CMP_NOT_EQUALS;
257                         break;
258
259                 case '~':
260                         input++;
261                         *token = POLICY_LEX_RX_NOT_EQUALS;
262                         break;
263
264                 case '*':
265                         input++;
266                         *token = POLICY_LEX_CMP_FALSE;
267                         break;
268
269                 default:
270                         *token = POLICY_LEX_L_NOT;
271                 }
272                 return input + 1;
273                 
274         case '=':
275                 switch (input[1]) {
276                 case '=':
277                         input++;
278                         *token = POLICY_LEX_CMP_EQUALS;
279                         break;
280
281                 case '~':
282                         input++;
283                         *token = POLICY_LEX_RX_EQUALS;
284                         break;
285
286                 case '*':
287                         input++;
288                         *token = POLICY_LEX_CMP_TRUE;
289                         break;
290
291                 default:
292                         *token = POLICY_LEX_ASSIGN;
293                 }
294                 return input + 1;
295                 
296         case '<':
297                 if (input[1] == '=') {
298                         input++;
299                         *token = POLICY_LEX_LE;
300                 } else {
301                         *token = POLICY_LEX_LT;
302                 }
303                 return input + 1;
304                 
305         case '>':
306                 if (input[1] == '=') {
307                         input++;
308                         *token = POLICY_LEX_GE;
309                 } else {
310                         *token = POLICY_LEX_GT;
311                 }
312                 return input + 1;
313
314         case '"':
315                 if (buflen < 2) {
316                         *token = POLICY_LEX_BAD;
317                         return input + 1;
318                 }
319                 
320                 input++;
321                 while (*input != '"') {
322                         /*
323                          *      Strings can't pass EOL.
324                          */
325                         if (!*input) {
326                                 return POLICY_LEX_BAD;
327                         }
328
329                         /*
330                          *      FIXME: Embedded quotes?
331                          */
332                         *(buffer++) = *(input++);
333                         buflen--;
334                         
335                         /*
336                          *      FIXME: Print more warnings?
337                          */
338                         if (buflen == 1) {
339                                 break;
340                         }
341                 }
342                 *buffer = '\0';
343                 
344                 *token = POLICY_LEX_DOUBLE_QUOTED_STRING;
345                 return input + 1; /* skip trailing '"' */
346
347         default:                /* bare word */
348                 break;
349         }
350
351         /*
352          *      It's a bare word, with nowhere to put it.  Die.
353          */
354         if (!buffer) {
355                 *token = POLICY_LEX_BAD;
356                 return input + 1;
357         }
358         
359         /*
360          *      Getting one character is stupid.
361          */
362         if (buflen < 2) {
363                 *token = POLICY_LEX_BAD;
364                 return input + 1;
365         }
366         
367         /*
368          *      Bare words are [-a-zA-Z0-9.]+
369          */
370         while (*input) {
371                 if (!(((*input >= '0') && (*input <= '9')) ||
372                       ((*input >= 'a') && (*input <= 'z')) ||
373                       ((*input >= 'A') && (*input <= 'Z')) ||
374                       (*input == '-') || (*input == '.') ||
375                       (*input == ':') || (*input == '_'))) {
376                         break;
377                 }
378                 *(buffer++) = *(input++);
379                 buflen--;
380                 
381                 /*
382                  *      FIXME: Print more warnings?
383                  */
384                 if (buflen == 1) {
385                         break;
386                 }
387         }
388         *buffer = '\0';
389         
390         *token = POLICY_LEX_BARE_WORD;
391         return input;
392 }
393
394
395 /*
396  *      We want to lexically analyze a file, so we need a wrapper
397  *      around the lexical analysis of strings.
398  */
399 typedef struct policy_lex_file_t {
400         FILE            *fp;
401         const char      *parse;
402         const char      *filename;
403         int             lineno;
404         int             debug;
405         rbtree_t        *policies;
406         policy_lex_t    token;
407         char            buffer[1024];
408 } policy_lex_file_t;
409
410
411 #define POLICY_LEX_FLAG_RETURN_EOL  (1 << 0)
412 #define POLICY_LEX_FLAG_PEEK        (1 << 1)
413 #define POLICY_LEX_FLAG_PRINT_TOKEN (1 << 2)
414
415 #define debug_tokens if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) printf
416
417
418 /*
419  *      Function to return a token saying what it read, and possibly
420  *      a buffer of the quoted string or bare word.
421  */
422 static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
423                                     int flags,
424                                     char *mystring, size_t mystringlen)
425 {
426         policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */
427
428         if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
429                 flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
430         }
431
432         if (!lexer->fp) {
433                 return POLICY_LEX_EOF;
434         }
435
436         /*
437          *      Starting off, the buffer needs to be primed.
438          */
439         if (!lexer->parse) {
440                 lexer->parse = fgets(lexer->buffer,
441                                      sizeof(lexer->buffer),
442                                      lexer->fp);
443                 
444                 if (!lexer->parse) {
445                         return POLICY_LEX_EOF;
446                 }
447
448                 lexer->lineno = 1;
449         } /* buffer is primed, read stuff */
450
451         if (lexer->token != POLICY_LEX_BAD) {
452                 token = lexer->token;
453                 lexer->token = POLICY_LEX_BAD;
454                 return token;
455         }
456
457         /*
458          *      Ignore whitespace, and keep filling the buffer
459          */
460         while (lexer->parse) {
461                 const char *next;
462
463                 next = policy_lex_string(lexer->parse, &token,
464                                          mystring, mystringlen);
465                 switch (token) {
466                 case POLICY_LEX_WHITESPACE: /* skip whitespace */
467                         lexer->parse = next;
468                         continue;
469
470                 case POLICY_LEX_EOL: /* read another line */
471                         lexer->parse = fgets(lexer->buffer,
472                                              sizeof(lexer->buffer),
473                                              lexer->fp);
474                         lexer->lineno++;
475                         if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
476                                 return POLICY_LEX_EOL;
477                         }
478                         break;  /* read another token */
479
480                 default:        /* return the token */
481                         if (!(flags & POLICY_LEX_FLAG_PEEK)) {
482                                 lexer->parse = next;
483                         }
484                         if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
485                                 debug_tokens("[%s token %s] ",
486                                              (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
487                                              lrad_int2str(rlm_policy_tokens,
488                                                           token, "?"));
489                         }
490                         return token;
491                         break;
492                 }
493         } /* loop until EOF */
494
495         /*
496          *      Close it for the user.
497          */
498         fclose(lexer->fp);
499         lexer->fp = NULL;
500
501         return POLICY_LEX_EOF;
502 }
503
504
505 /*
506  *      Push a token back onto the input.
507  *
508  *      FIXME: Push words, too?
509  */
510 static int policy_lex_push_token(policy_lex_file_t *lexer,
511                                  policy_lex_t token)
512 {
513         if (lexer->token != POLICY_LEX_BAD) {
514                 rad_assert(0 == 1);
515                 return 0;
516         }
517
518         lexer->token = token;
519         return 1;
520 }
521
522
523 /*
524  *      Forward declarations.
525  */
526 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail);
527
528
529 /*
530  *      Map reserved words to tokens, and vice versa.
531  */
532 const LRAD_NAME_NUMBER policy_reserved_words[] = {
533         { "if", POLICY_RESERVED_IF },
534         { "else", POLICY_RESERVED_ELSE },
535         { "debug", POLICY_RESERVED_DEBUG },
536         { "print", POLICY_RESERVED_PRINT },
537         { "policy", POLICY_RESERVED_POLICY },
538         { "control", POLICY_RESERVED_CONTROL },
539         { "request", POLICY_RESERVED_REQUEST },
540         { "reply", POLICY_RESERVED_REPLY },
541         { "proxy-request", POLICY_RESERVED_PROXY_REQUEST },
542         { "proxy-reply", POLICY_RESERVED_PROXY_REPLY },
543         { "include", POLICY_RESERVED_INCLUDE },
544         { "return", POLICY_RESERVED_RETURN },
545         { "module", POLICY_RESERVED_MODULE },
546         { NULL, POLICY_RESERVED_UNKNOWN }
547 };
548
549
550 /*
551  *      Simplifies some later coding
552  */
553 static int policy_lex_str2int(policy_lex_file_t *lexer,
554                               const LRAD_NAME_NUMBER *table, int default_value)
555 {
556         policy_lex_t token;
557         char buffer[256];
558
559         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
560         if (token != POLICY_LEX_BARE_WORD) {
561                 fprintf(stderr, "%s[%d]: Unexpected token\n",
562                         lexer->filename, lexer->lineno);
563                 return default_value;
564         }
565
566         return lrad_str2int(table, buffer, default_value);
567 }
568
569
570 /*
571  *      print foo
572  *      print "foo"
573  */
574 static int parse_print(policy_lex_file_t *lexer, policy_item_t **tail)
575 {
576         policy_lex_t token;
577         char mystring[1024];
578         policy_print_t *this;
579
580         debug_tokens("[PRINT] ");
581
582         this = rad_malloc(sizeof(*this));
583         memset(this, 0, sizeof(*this));
584
585         this->item.type = POLICY_TYPE_PRINT;
586         this->item.lineno = lexer->lineno;
587
588         token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
589         if ((token != POLICY_LEX_BARE_WORD) &&
590             (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
591                 fprintf(stderr, "%s[%d]: Bad print command\n",
592                         lexer->filename, lexer->lineno);
593                 return 0;
594         }
595
596         this->rhs_type = token;
597         this->rhs = strdup(mystring);
598
599         *tail = (policy_item_t *) this;
600         
601         return 1;
602 }
603
604
605 /*
606  * (foo == bar), with nested conditionals.
607  */
608 static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
609 {
610         int rcode;
611         policy_lex_t token, compare;
612         char lhs[256], rhs[256];
613         policy_condition_t *this;
614
615         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
616         if (token != POLICY_LEX_L_BRACKET) {
617                 fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
618                         lexer->filename, lexer->lineno,
619                         lrad_int2str(rlm_policy_tokens, token, lhs));
620                 return 0;
621         }
622
623         this = rad_malloc(sizeof(*this));
624         memset(this, 0, sizeof(*this));
625
626         this->item.type = POLICY_TYPE_CONDITIONAL;
627         this->item.lineno = lexer->lineno;
628
629         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
630         switch (token) {
631         case POLICY_LEX_L_BRACKET:
632                 if (!policy_lex_push_token(lexer, token)) {
633                         rlm_policy_free_item((policy_item_t *) this);
634                         return 0;
635                 }
636                 
637                 this->compare = POLICY_LEX_L_BRACKET;
638                 this->child_condition = POLICY_LEX_L_BRACKET;
639                 rcode = parse_condition(lexer, &(this->child));
640                 if (!rcode) {
641                         rlm_policy_free_item((policy_item_t *) this);
642                         return rcode;
643                 }
644                 break;
645
646         case POLICY_LEX_L_NOT:
647                 this->compare = POLICY_LEX_L_NOT;
648                 debug_tokens("[NOT] ");
649
650                 /*
651                  *      FIXME: allow !foo, !foo=bar, etc.
652                  *
653                  *      Maybe we should learn how to use lex && yacc?
654                  */
655
656                 rcode = parse_condition(lexer, &(this->child));
657                 if (!rcode) {
658                         rlm_policy_free_item((policy_item_t *) this);
659                         return rcode;
660                 }
661                 break;
662
663         case POLICY_LEX_BARE_WORD:
664                 this->lhs_type = token;
665                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
666                 if (token == POLICY_LEX_L_BRACKET) {
667                         debug_tokens("[IF-CALL %s] ", lhs);
668
669                         /*
670                          *      Function call.
671                          */
672                         if (rlm_policy_find(lexer->policies, lhs) == NULL) {
673                                 fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
674                                         lexer->filename, lexer->lineno,
675                                         lhs);
676                                 rlm_policy_free_item((policy_item_t *) this);
677                                 return 0;
678                                 
679                         }
680                         
681                         /*
682                          *      this->lhs set up below, after "check"
683                          */
684                         this->lhs_type = POLICY_LEX_FUNCTION;
685
686                         /*
687                          *      Copied from parse_call
688                          */
689                         token = policy_lex_file(lexer, 0, NULL, 0);
690                         if (token != POLICY_LEX_L_BRACKET) {
691                                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
692                                         lexer->filename, lexer->lineno,
693                                         lrad_int2str(rlm_policy_tokens, token, "?"));
694                                 return 0;
695                         }
696                         
697                         token = policy_lex_file(lexer, 0, NULL, 0);
698                         if (token != POLICY_LEX_R_BRACKET) {
699                                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
700                                         lexer->filename, lexer->lineno,
701                                         lrad_int2str(rlm_policy_tokens, token, "?"));
702                                 return 0;
703                         }
704                 } /* else it's a comparison? */
705                 goto check;
706
707         case POLICY_LEX_DOUBLE_QUOTED_STRING:
708                 this->lhs_type = token;
709
710                 /*
711                  *      Got word.  May just be test for existence.
712                  */
713         check:
714                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
715                 if (token == POLICY_LEX_R_BRACKET) {
716                         debug_tokens("[TEST %s] ", lhs);
717                         this->lhs = strdup(lhs);
718                         this->compare = POLICY_LEX_CMP_TRUE;
719                         break;
720                 }
721
722                 compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
723                 switch (compare) {
724                 case POLICY_LEX_CMP_EQUALS:
725                 case POLICY_LEX_CMP_NOT_EQUALS:
726                 case POLICY_LEX_RX_EQUALS:
727                 case POLICY_LEX_RX_NOT_EQUALS:
728                 case POLICY_LEX_CMP_TRUE:
729                 case POLICY_LEX_CMP_FALSE:
730                 case POLICY_LEX_LT:
731                 case POLICY_LEX_GT:
732                 case POLICY_LEX_LE:
733                 case POLICY_LEX_GE:
734                         break;
735
736                 default:
737                         fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
738                                 lexer->filename, lexer->lineno,
739                                 lrad_int2str(rlm_policy_tokens, compare, rhs));
740                         rlm_policy_free_item((policy_item_t *) this);
741                         return 0;
742                 }
743
744                 token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
745                 if ((token != POLICY_LEX_BARE_WORD) &&
746                     (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
747                         fprintf(stderr, "%s[%d]: Unexpected rhs token\n",
748                                 lexer->filename, lexer->lineno);
749                         rlm_policy_free_item((policy_item_t *) this);
750                         return 0;
751                 }
752                 debug_tokens("[COMPARE (%s %s %s)] ",
753                        lhs, lrad_int2str(rlm_policy_tokens, compare, "?"), rhs);
754                 this->lhs = strdup(lhs);
755                 this->compare = compare;
756                 this->rhs_type = token;
757                 this->rhs = strdup(rhs);
758                 break;
759
760         default:
761                 fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
762                         lexer->filename, lexer->lineno);
763                 rlm_policy_free_item((policy_item_t *) this);
764                 return 0;
765         }
766
767         token = policy_lex_file(lexer, 0, NULL, 0);
768         if (token != POLICY_LEX_R_BRACKET) {
769                 fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
770                         lexer->filename, lexer->lineno,
771                         lrad_int2str(rlm_policy_tokens, token, "?"));
772                 rlm_policy_free_item((policy_item_t *) this);
773                 return 0;
774         }
775
776         /*
777          *      After the end of condition, we MAY have && or ||
778          */
779         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
780         if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
781                 token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
782                 debug_tokens("[%s] ",
783                        lrad_int2str(rlm_policy_tokens, token, "?"));
784                 this->child_condition = token;
785                 rcode = parse_condition(lexer, &(this->child));
786                 if (!rcode) {
787                         rlm_policy_free_item((policy_item_t *) this);
788                         return 0;
789                 }
790         }
791
792         *tail = (policy_item_t *) this;
793
794         return 1;
795 }
796
797
798 /*
799  *      if (...) {...}
800  *      if (...) {...} else {...}
801  *      if (...) {...} else if ...
802  */
803 static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
804 {
805         int rcode;
806         policy_lex_t token;
807         char mystring[256];
808         policy_if_t *this;
809
810         debug_tokens("[IF] ");
811
812         this = rad_malloc(sizeof(*this));
813         memset(this, 0, sizeof(*this));
814
815         this->item.type = POLICY_TYPE_IF;
816         this->item.lineno = lexer->lineno;
817
818         rcode = parse_condition(lexer, &(this->condition));
819         if (!rcode) {
820                 rlm_policy_free_item((policy_item_t *) this);
821                 return rcode;
822         }
823
824         rcode = parse_block(lexer, &(this->if_true));
825         if (!rcode) {
826                 rlm_policy_free_item((policy_item_t *) this);
827                 return rcode;
828         }
829
830         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
831                                 mystring, sizeof(mystring));
832         if ((token == POLICY_LEX_BARE_WORD) &&
833             (lrad_str2int(policy_reserved_words, mystring,
834                           POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
835                 debug_tokens("[ELSE] ");
836                 token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
837
838                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
839                                         mystring, sizeof(mystring));
840                 if ((token == POLICY_LEX_BARE_WORD) &&
841                     (lrad_str2int(policy_reserved_words, mystring,
842                                   POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
843                         token = policy_lex_file(lexer, 0,
844                                                 mystring, sizeof(mystring));
845                         rcode = parse_if(lexer, &(this->if_false));
846                 } else {
847                         rcode = parse_block(lexer, &(this->if_false));
848                 }
849                 if (!rcode) {
850                         rlm_policy_free_item((policy_item_t *) this);
851                         return rcode;
852                 }
853         }
854
855         debug_tokens("\n");
856
857         /*
858          *      Empty "if" condition, don't even bother remembering
859          *      it.
860          */
861         if (!this->if_true && !this->if_false) {
862                 debug_tokens("Discarding empty \"if\" statement at line %d\n",
863                              this->item.lineno);
864                 rlm_policy_free_item((policy_item_t *) this);
865                 return 1;
866         }
867
868         *tail = (policy_item_t *) this;
869         
870         return 1;
871 }
872
873
874 /*
875  *      Parse a reference to a named policy "foo()"
876  */
877 static int parse_call(policy_lex_file_t *lexer, policy_item_t **tail,
878                       const char *name)
879 {
880         policy_lex_t token;
881         policy_call_t *this;
882
883         debug_tokens("[CALL] ");
884
885         token = policy_lex_file(lexer, 0, NULL, 0);
886         if (token != POLICY_LEX_L_BRACKET) {
887                 fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
888                         lexer->filename, lexer->lineno,
889                         lrad_int2str(rlm_policy_tokens, token, "?"));
890                 return 0;
891         }
892
893         token = policy_lex_file(lexer, 0, NULL, 0);
894         if (token != POLICY_LEX_R_BRACKET) {
895                 fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
896                         lexer->filename, lexer->lineno,
897                         lrad_int2str(rlm_policy_tokens, token, "?"));
898                 return 0;
899         }
900
901         this = rad_malloc(sizeof(*this));
902         memset(this, 0, sizeof(*this));
903
904         this->item.type = POLICY_TYPE_CALL;
905         this->item.lineno = lexer->lineno;
906
907         this->name = strdup(name);
908
909         *tail = (policy_item_t *) this;
910
911         return 1;
912 }
913
914
915 /*
916  *      Edit/update/replace an attribute list
917  */
918 static int parse_attribute_block(policy_lex_file_t *lexer,
919                                  policy_item_t **tail,
920                                  policy_reserved_word_t where)
921 {
922         policy_lex_t token;
923         policy_attributes_t *this;
924         char buffer[32];
925         
926         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
927         switch (token) {
928         case POLICY_LEX_ASSIGN:
929         case POLICY_LEX_SET_EQUALS:
930         case POLICY_LEX_CONCAT_EQUALS:
931                 break;
932
933         default:
934                 fprintf(stderr, "%s[%d]: Unexpected token %s\n",
935                         lexer->filename, lexer->lineno,
936                         lrad_int2str(rlm_policy_tokens, token, "?"));
937                 return 0;       /* unknown */
938         }
939         
940         this = rad_malloc(sizeof(*this));
941         memset(this, 0, sizeof(*this));
942
943         this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
944         this->item.lineno = lexer->lineno;
945         this->where = where;
946         this->how = token;
947
948         if (!parse_block(lexer, &(this->attributes))) {
949                 rlm_policy_free_item((policy_item_t *) this);
950                 return 0;
951         }
952
953         *tail = (policy_item_t *) this;
954         return 1;
955 }
956
957
958 /*
959  *      Parse a return statement.
960  */
961 static int parse_return(policy_lex_file_t *lexer, policy_item_t **tail)
962 {
963         int rcode;
964         policy_lex_t token;
965         policy_return_t *this;
966
967         rcode = policy_lex_str2int(lexer, policy_return_codes,
968                                    RLM_MODULE_NUMCODES);
969         if (rcode == RLM_MODULE_NUMCODES) {
970                 fprintf(stderr, "%s[%d]: Invalid return code\n",
971                         lexer->filename, lexer->lineno);
972                 return 0;
973         }
974
975         /*
976          *      Look for more sutff
977          */
978         token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
979                                 NULL, sizeof(0));
980         if (token != POLICY_LEX_RC_BRACKET) {
981                 fprintf(stderr, "%s[%d]: return statement must be the last statement in a policy.\n",
982                         lexer->filename, lexer->lineno);
983                 return 0;
984         }
985
986         this = rad_malloc(sizeof(*this));
987         memset(this, 0, sizeof(*this));
988
989         this->item.type = POLICY_TYPE_RETURN;
990         this->item.lineno = lexer->lineno;
991         this->rcode = rcode;
992
993         *tail = (policy_item_t *) this;
994
995         return 1;
996 }
997
998
999 const LRAD_NAME_NUMBER policy_component_names[] = {
1000         { "authenticate", RLM_COMPONENT_AUTH },
1001         { "authorize", RLM_COMPONENT_AUTZ },
1002         { "preacct", RLM_COMPONENT_PREACCT },
1003         { "accounting", RLM_COMPONENT_ACCT },
1004         { "session", RLM_COMPONENT_SESS },
1005         { "pre-proxy", RLM_COMPONENT_PRE_PROXY },
1006         { "post-proxy", RLM_COMPONENT_POST_PROXY },
1007         { "post-auth", RLM_COMPONENT_POST_AUTH },
1008         { NULL, RLM_COMPONENT_COUNT }
1009 };
1010
1011 /*
1012  *      Parse a module statement.
1013  */
1014 static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
1015 {
1016         int component;
1017         policy_lex_t token;
1018         policy_module_t *this;
1019         char *p;
1020         const char *section_name;
1021         char filename[1024];
1022         char buffer[2048];
1023         CONF_SECTION *cs, *subcs;
1024         modcallable *mc;
1025
1026         /*
1027          *      And the filename
1028          */
1029         token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1030         if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1031                 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1032                         lexer->filename, lexer->lineno,
1033                         lrad_int2str(rlm_policy_tokens, token, "?"));
1034                 return 0;
1035         }
1036
1037         /*
1038          *      See if we're including all of the files in a subdirectory.
1039          */
1040         strNcpy(buffer, lexer->filename, sizeof(buffer));
1041         p = strrchr(buffer, '/');
1042         if (p) {
1043                 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1044         } else {
1045                 snprintf(buffer, sizeof(buffer), "%s/%s",
1046                          radius_dir, filename);
1047         }
1048         
1049         /*
1050          *      Include section calling a module.
1051          */
1052         debug_tokens("including module section from file %s\n", buffer);
1053         cs = conf_read(lexer->filename, lexer->lineno, buffer, NULL);
1054         if (!cs) {
1055                 return 0;       /* it prints out error messages */
1056         }
1057
1058         /*
1059          *      The outer section is called "main", and can be ignored.
1060          *      It should be a section, so there should be a subsection.
1061          */
1062         subcs = cf_subsection_find_next(cs, NULL, NULL);
1063         if (!subcs) {
1064                 fprintf(stderr, "%s[%d]: Expected section containing modules\n",
1065                         lexer->filename, lexer->lineno);
1066                 cf_section_free(&cs);
1067                 return 0;
1068         }
1069
1070         section_name = cf_section_name1(subcs);
1071         rad_assert(section_name != NULL);
1072         component = lrad_str2int(policy_component_names, section_name,
1073                                  RLM_COMPONENT_COUNT);
1074         if (component == RLM_COMPONENT_COUNT) {
1075                 fprintf(stderr, "%s[%d]: Invalid section name \"%s\"\n",
1076                         lexer->filename, lexer->lineno, section_name);
1077                 cf_section_free(&cs);
1078                 return 0;
1079         }
1080
1081         /*
1082          *      Compile the module entry.
1083          */
1084         mc = compile_modgroup(component, subcs, buffer);
1085         if (!mc) {
1086                 cf_section_free(&cs);
1087                 return 0;       /* more often results in calling exit... */
1088         }
1089
1090         this = rad_malloc(sizeof(*this));
1091         memset(this, 0, sizeof(*this));
1092
1093         this->item.type = POLICY_TYPE_MODULE;
1094         this->item.lineno = lexer->lineno;
1095         this->component = component;
1096         this->cs = cs;
1097         this->mc = mc;
1098
1099         *tail = (policy_item_t *) this;
1100
1101         return 1;
1102 }
1103
1104
1105 /*
1106  *      Parse one statement.  'foo = bar', or 'if (...) {...}', or '{...}',
1107  *      and so on.
1108  */
1109 static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
1110 {
1111         int rcode;
1112         policy_reserved_word_t reserved;
1113         policy_lex_t token, assign;
1114         char lhs[256], rhs[256];
1115         policy_assignment_t *this;
1116
1117         /*
1118          *      See what kind of token we have.
1119          */
1120         token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
1121         switch (token) {
1122         case POLICY_LEX_LC_BRACKET:
1123                 rcode = parse_block(lexer, tail);
1124                 if (!rcode) {
1125                         return 0;
1126                 }
1127                 break;
1128
1129         case POLICY_LEX_BARE_WORD:
1130                 reserved = lrad_str2int(policy_reserved_words,
1131                                         lhs,
1132                                         POLICY_RESERVED_UNKNOWN);
1133                 switch (reserved) {
1134                 case POLICY_RESERVED_IF:
1135                         if (parse_if(lexer, tail)) {
1136                                 return 1;
1137                         }
1138                         return 0;
1139                         break;
1140                         
1141                 case POLICY_RESERVED_CONTROL:
1142                 case POLICY_RESERVED_REQUEST:
1143                 case POLICY_RESERVED_REPLY:
1144                 case POLICY_RESERVED_PROXY_REQUEST:
1145                 case POLICY_RESERVED_PROXY_REPLY:
1146                         if (parse_attribute_block(lexer, tail,
1147                                                   reserved))
1148                                 return 1;
1149                         return 0;
1150                         break;
1151                         
1152                 case POLICY_RESERVED_PRINT:
1153                         if (parse_print(lexer, tail)) {
1154                                 return 1;
1155                         }
1156                         return 0;
1157                         break;
1158                         
1159                 case POLICY_RESERVED_RETURN:
1160                         if (parse_return(lexer, tail)) {
1161                                 return 1;
1162                         }
1163                         return 0;
1164                         break;
1165                         
1166                 case POLICY_RESERVED_MODULE:
1167                         if (parse_module(lexer, tail)) {
1168                                 return 1;
1169                         }
1170                         return 0;
1171                         break;
1172
1173                 case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
1174                         /*
1175                          *      Is a named policy, parse the reference to it.
1176                          */
1177                         if (rlm_policy_find(lexer->policies, lhs) != NULL) {
1178                                 if (!parse_call(lexer, tail, lhs)) {
1179                                         return 0;
1180                                 }
1181                                 return 1;
1182                         }
1183
1184                         {
1185                                 const DICT_ATTR *dattr;
1186                                 
1187                                 /*
1188                                  *      Bare words MUST be dictionary attributes
1189                                  */
1190                                 
1191                                 dattr = dict_attrbyname(lhs);
1192                                 if (!dattr) {
1193                                         fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
1194                                                 lexer->filename, lexer->lineno, lhs);
1195                                         return 0;
1196                                 }
1197                                 debug_tokens("%s[%d]: Got attribute %s\n",
1198                                              lexer->filename, lexer->lineno,
1199                                              lhs);
1200                         }
1201                         break;
1202                         
1203                 default:
1204                         fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
1205                                 lexer->filename, lexer->lineno, lhs);
1206                         return 0;
1207                 } /* switch over reserved words */
1208                 break;
1209                 
1210                 /*
1211                  *      Return from nested blocks.
1212                  */
1213         case POLICY_LEX_RC_BRACKET:
1214                 policy_lex_push_token(lexer, token);
1215                 return 2;       /* magic */
1216
1217         case POLICY_LEX_EOF:    /* nothing more to do */
1218                 return 3;
1219
1220         default:
1221                 fprintf(stderr, "%s[%d]: Unexpected %s\n",
1222                         lexer->filename, lexer->lineno,
1223                         lrad_int2str(policy_explanations,
1224                                      token, "string"));
1225                 break;
1226         }
1227
1228         /*
1229          *      Parse a bare statement.
1230          */
1231         assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1232         switch (assign) {
1233         case POLICY_LEX_ASSIGN:
1234         case POLICY_LEX_SET_EQUALS:
1235         case POLICY_LEX_AND_EQUALS:
1236         case POLICY_LEX_OR_EQUALS:
1237         case POLICY_LEX_PLUS_EQUALS:
1238                 break;
1239
1240         default:
1241                 fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
1242                         lexer->filename, lexer->lineno,
1243                         lrad_int2str(policy_explanations,
1244                                      assign, "string"));
1245                 return 0;
1246         }
1247
1248         this = rad_malloc(sizeof(*this));
1249         memset(this, 0, sizeof(*this));
1250
1251         this->item.type = POLICY_TYPE_ASSIGNMENT;
1252         this->item.lineno = lexer->lineno;
1253
1254         token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
1255         if ((token != POLICY_LEX_BARE_WORD) &&
1256             (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
1257                 fprintf(stderr, "%s[%d]: Unexpected rhs %s\n",
1258                         lexer->filename, lexer->lineno,
1259                         lrad_int2str(policy_explanations,
1260                                      token, "string"));
1261                 rlm_policy_free_item((policy_item_t *) this);
1262                 return 0;
1263         }
1264         this->rhs_type = token;
1265         this->rhs = strdup(rhs);
1266
1267         token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1268                                 rhs, sizeof(rhs));
1269         if (token != POLICY_LEX_EOL) {
1270                 fprintf(stderr, "%s[%d]: Expected EOL\n",
1271                         lexer->filename, lexer->lineno);
1272                 rlm_policy_free_item((policy_item_t *) this);
1273                 return 0;
1274         }
1275         debug_tokens("[ASSIGN %s %s %s]\n",
1276                lhs, lrad_int2str(rlm_policy_tokens, assign, "?"), rhs);
1277
1278         /*
1279          *      Fill in the assignment struct
1280          */
1281         this->lhs = strdup(lhs);
1282         this->assign = assign;
1283
1284         *tail = (policy_item_t *) this;
1285
1286         return 1;
1287 }
1288
1289
1290 /*
1291  *      Parse block of statements.  The block has already been checked
1292  *      to begin with a '{'.
1293  */
1294 static int parse_block(policy_lex_file_t *lexer, policy_item_t **tail)
1295 {
1296         int rcode;
1297         policy_lex_t token;
1298
1299         debug_tokens("[BLOCK] ");
1300
1301         token = policy_lex_file(lexer, 0, NULL, 0);
1302         if (token != POLICY_LEX_LC_BRACKET) {
1303                 fprintf(stderr, "%s[%d]: Expected '{'\n",
1304                         lexer->filename, lexer->lineno);
1305                 return 0;
1306         }
1307
1308         rcode = 0;
1309         while ((rcode = parse_statement(lexer, tail)) != 0) {
1310                 if (rcode == 2) {
1311                         token = policy_lex_file(lexer, 0, NULL, 0);
1312                         if (token != POLICY_LEX_RC_BRACKET) {
1313                                 fprintf(stderr, "%s[%d]: Expected '}'\n",
1314                                         lexer->filename, lexer->lineno);
1315                                 return 0;
1316                         }
1317                         return 1;
1318                 }
1319                 rad_assert(*tail != NULL);
1320                 /* parse_statement must fill this in */
1321                 while (*tail) tail = &((*tail)->next);
1322         }
1323         debug_tokens("\n");
1324
1325         /*
1326          *      Parse statement failed.
1327          */
1328         return 0;
1329 }
1330
1331
1332 /*
1333  *      Parse debugging statements
1334  */
1335 static int parse_debug(policy_lex_file_t *lexer)
1336 {
1337         int rcode = 0;
1338         policy_lex_t token;
1339         char buffer[32];
1340
1341         token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1342         if (token != POLICY_LEX_BARE_WORD) {
1343                 fprintf(stderr, "%s[%d]: Bad debug command\n",
1344                         lexer->filename, lexer->lineno);
1345                 return 0;
1346         }
1347
1348         if (strcasecmp(buffer, "none") == 0) {
1349                 lexer->debug = POLICY_DEBUG_NONE;
1350                 rcode = 1;
1351
1352         } else if (strcasecmp(buffer, "peek") == 0) {
1353                 lexer->debug |= POLICY_DEBUG_PEEK;
1354                 rcode = 1;
1355
1356         } else if (strcasecmp(buffer, "print_tokens") == 0) {
1357                 lexer->debug |= POLICY_DEBUG_PRINT_TOKENS;
1358                 rcode = 1;
1359
1360         } else if (strcasecmp(buffer, "print_policy") == 0) {
1361                 lexer->debug |= POLICY_DEBUG_PRINT_POLICY;
1362                 rcode = 1;
1363
1364         } else if (strcasecmp(buffer, "evaluate") == 0) {
1365                 lexer->debug |= POLICY_DEBUG_EVALUATE;
1366                 rcode = 1;
1367         }
1368
1369         if (rcode) {
1370                 token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
1371                                         NULL, 0);
1372                 if (token != POLICY_LEX_EOL) {
1373                         fprintf(stderr, "%s[%d]: Expected EOL\n",
1374                                 lexer->filename, lexer->lineno);
1375                         return 0;
1376                 }
1377         } else {
1378                 fprintf(stderr, "%s[%d]: Bad debug command \"%s\"\n",
1379                         lexer->filename, lexer->lineno, buffer);
1380                 return 0;
1381         }
1382
1383         return 1;
1384 }
1385
1386
1387 /*
1388  *      Parse a named policy "policy foo {...}"
1389  */
1390 static int parse_named_policy(policy_lex_file_t *lexer)
1391 {
1392         int rcode;
1393         policy_lex_t token;
1394         char mystring[256];
1395         policy_named_t *this;
1396         DICT_ATTR *dattr;
1397
1398         debug_tokens("[POLICY] ");
1399
1400         this = rad_malloc(sizeof(*this));
1401         memset(this, 0, sizeof(*this));
1402
1403         this->item.type = POLICY_TYPE_NAMED_POLICY;
1404         this->item.lineno = lexer->lineno;
1405
1406         token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
1407         if (token != POLICY_LEX_BARE_WORD) {
1408                 fprintf(stderr, "%s[%d]: Expected policy name, got \"%s\"\n",
1409                         lexer->filename, lexer->lineno,
1410                         lrad_int2str(rlm_policy_tokens, token, "?"));
1411                 rlm_policy_free_item((policy_item_t *) this);
1412                 return 0;
1413         }
1414
1415         dattr = dict_attrbyname(mystring);
1416         if (dattr) {
1417                 fprintf(stderr, "%s[%d]: Invalid policy name \"%s\": it is already defined as a dictionary attribute\n",
1418                         lexer->filename, lexer->lineno, mystring);
1419                 rlm_policy_free_item((policy_item_t *) this);
1420                 return 0;
1421         }
1422
1423         this->name = strdup(mystring);
1424         rcode = parse_block(lexer, &(this->policy));
1425         if (!rcode) {
1426                 rlm_policy_free_item((policy_item_t *) this);
1427                 return rcode;
1428         }
1429
1430         /*
1431          *      And insert it into the tree of policies.
1432          *
1433          *      For now, policy names aren't scoped, they're global.
1434          */
1435         if (!rlm_policy_insert(lexer->policies, this)) {
1436                 fprintf(stderr, "Failed to insert policy \"%s\"\n", this->name);
1437                 rlm_policy_free_item((policy_item_t *) this);
1438                 return 0;
1439         }
1440
1441         /*
1442          *      Do NOT add it into the list of parsed expressions!
1443          *      The above insertion will take care of freeing it if
1444          *      anything goes wrong...
1445          */
1446         return 1;
1447 }
1448
1449
1450 /*
1451  *      Parse an "include filename" statement
1452  *
1453  *      FIXME: Tie this file into the CONF_SECTION for HUP handling!
1454  */
1455 static int parse_include(policy_lex_file_t *lexer)
1456 {
1457         char *p;
1458         policy_lex_t token;
1459         char filename[1024];
1460         char buffer[2048];
1461
1462         token = policy_lex_file(lexer, 0, filename, sizeof(filename));
1463         if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
1464                 fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
1465                         lexer->filename, lexer->lineno,
1466                         lrad_int2str(rlm_policy_tokens, token, "?"));
1467                 return 0;
1468         }
1469
1470         /*
1471          *      See if we're including all of the files in a subdirectory.
1472          */
1473         strNcpy(buffer, lexer->filename, sizeof(buffer));
1474         p = strrchr(buffer, '/');
1475         if (p) {
1476                 strNcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
1477
1478 #ifdef HAVE_DIRENT_H    
1479                 p = strrchr(p + 1, '/');
1480                 if (p && !p[1]) {
1481                         DIR             *dir;
1482                         struct dirent   *dp;
1483                         
1484                         p++;
1485
1486                         dir = opendir(buffer);
1487                         if (!dir) {
1488                                 fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
1489                                         lexer->filename, lexer->lineno,
1490                                         buffer, strerror(errno));
1491                                 return 0;
1492                         }
1493                         
1494                         /*
1495                          *      Read the directory, ignoring "." files.
1496                          */
1497                         while ((dp = readdir(dir)) != NULL) {
1498                                 struct stat buf;
1499
1500                                 if (dp->d_name[0] == '.') continue;
1501                                 if (strchr(dp->d_name, '~') != NULL) continue;
1502
1503                                 strNcpy(p, dp->d_name,
1504                                         sizeof(buffer) - (p - buffer));
1505
1506                                 if ((stat(buffer, &buf) != 0) ||
1507                                     S_ISDIR(buf.st_mode)) continue;
1508
1509                                 debug_tokens("\nincluding file %s\n", buffer);
1510                                 if (!rlm_policy_parse(lexer->policies, buffer)) {
1511                                         closedir(dir);
1512                                         return 0;
1513                                 }
1514                         }
1515                         closedir(dir);
1516                         return 1;
1517                 } /* else it must have been a normalx file */
1518 #endif
1519         } else {
1520                 snprintf(buffer, sizeof(buffer), "%s/%s",
1521                          radius_dir, filename);
1522         }
1523         
1524         /*
1525          *      Handle one include file.
1526          */
1527         debug_tokens("\nincluding file %s\n", buffer);
1528         if (!rlm_policy_parse(lexer->policies, buffer)) {
1529                 return 0;
1530         }
1531
1532         return 1;
1533 }
1534
1535
1536 /*
1537  *      Parse data from a file into a policy language.
1538  */
1539 int rlm_policy_parse(rbtree_t *policies, const char *filename)
1540 {
1541         FILE *fp;
1542         policy_lex_t token;
1543         policy_lex_file_t mylexer, *lexer = NULL;
1544         char buffer[32];
1545
1546         fp = fopen(filename, "r");
1547         if (!fp) {
1548                 fprintf(stderr, "Failed to open %s: %s\n",
1549                         filename, strerror(errno));
1550                 return 0;
1551         }
1552
1553         lexer = &mylexer;
1554         memset(lexer, 0, sizeof(*lexer));
1555         lexer->filename = filename;
1556         lexer->fp = fp;
1557         lexer->token = POLICY_LEX_BAD;
1558         lexer->parse = NULL;    /* initial input */
1559         lexer->policies = policies;
1560
1561         do {
1562                 int reserved;
1563
1564                 token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
1565                 switch (token) {
1566                 case POLICY_LEX_BARE_WORD:
1567                         reserved = lrad_str2int(policy_reserved_words,
1568                                                 buffer,
1569                                                 POLICY_RESERVED_UNKNOWN);
1570                         switch (reserved) {
1571                         case POLICY_RESERVED_POLICY:
1572                                 if (!parse_named_policy(lexer)) {
1573                                         return 0;
1574                                 }
1575                                 break;
1576                                 
1577                         case POLICY_RESERVED_INCLUDE:
1578                                 if (!parse_include(lexer)) {
1579                                         return 0;
1580                                 }
1581                                 break;
1582                                 
1583                         case POLICY_RESERVED_DEBUG:
1584                                 if (!parse_debug(lexer)) {
1585                                         return 0;
1586                                 }
1587                                 break;
1588                         
1589                         default:
1590                                 fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
1591                                         lexer->filename, lexer->lineno,
1592                                         buffer);
1593                                 return 0;
1594                                 break;
1595                         } /* switch over reserved words */
1596
1597                 case POLICY_LEX_EOF:
1598                         break;
1599
1600                 default:
1601                         fprintf(stderr, "%s[%d]: Illegal input\n",
1602                                 lexer->filename, lexer->lineno);
1603                         return 0;
1604                 }
1605
1606                 fflush(stdout);
1607         } while (token != POLICY_LEX_EOF);
1608
1609
1610         debug_tokens("--------------------------------------------------\n");
1611
1612         return 1;
1613 }
1614