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