e707a51a19751dd38ff722214bfb8fd45e83a189
[freeradius.git] / src / main / files.c
1 /*
2  * files.c      Read config files into memory.
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 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include <sys/stat.h>
32
33 #include <ctype.h>
34 #include <fcntl.h>
35
36 /*
37  *      Free a PAIR_LIST
38  */
39 void pairlist_free(PAIR_LIST **pl)
40 {
41         PAIR_LIST *p, *next;
42
43         for (p = *pl; p; p = next) {
44                 /* name is allocated contiguous with p */
45                 if (p->check) pairfree(&p->check);
46                 if (p->reply) pairfree(&p->reply);
47                 next = p->next;
48                 free(p);
49         }
50         *pl = NULL;
51 }
52
53
54 #define FIND_MODE_NAME  0
55 #define FIND_MODE_REPLY 1
56
57 /*
58  *      Read the users, huntgroups or hints file.
59  *      Return a PAIR_LIST.
60  */
61 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
62 {
63         FILE *fp;
64         int mode = FIND_MODE_NAME;
65         char entry[256];
66         char buffer[8192];
67         const char *ptr;
68         VALUE_PAIR *check_tmp;
69         VALUE_PAIR *reply_tmp;
70         PAIR_LIST *pl = NULL, *t;
71         PAIR_LIST **last = &pl;
72         int lineno = 0;
73         int old_lineno = 0;
74         FR_TOKEN parsecode;
75         char newfile[8192];
76
77         DEBUG2("reading pairlist file %s", file);
78         
79         /*
80          *      Open the file.  The error message should be a little
81          *      more useful...
82          */
83         if ((fp = fopen(file, "r")) == NULL) {
84                 if (!complain)
85                         return -1;
86                 radlog(L_ERR, "Couldn't open %s for reading: %s",
87                                 file, strerror(errno));
88                 return -1;
89         }
90
91         parsecode = T_EOL;
92
93         /*
94          *      Read the entire file into memory for speed.
95          */
96         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
97                 lineno++;
98                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
99                         fclose(fp);
100                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
101                         pairlist_free(&pl);
102                         return -1;
103                 }
104                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
105
106                 /*
107                  *      If the line contains nothing but whitespace,
108                  *      ignore it.
109                  */
110                 ptr = buffer;
111                 while (isspace((int) *ptr)) ptr++;
112                 if (*ptr == '\0') continue;
113
114 parse_again:
115                 if(mode == FIND_MODE_NAME) {
116                         /*
117                          *      Find the entry starting with the users name
118                          */
119                         if (isspace((int) buffer[0]))  {
120                                 if (parsecode != T_EOL) {
121                                         radlog(L_ERR,
122                                                "%s[%d]: Unexpected trailing comma for entry %s",
123                                                file, lineno, entry);
124                                         fclose(fp);
125                                         return -1;
126                                 }
127                                 continue;
128                         }
129
130                         ptr = buffer;
131                         getword(&ptr, entry, sizeof(entry));
132
133                         /*
134                          *      Include another file if we see
135                          *      $INCLUDE filename
136                          */
137                         if (strcasecmp(entry, "$INCLUDE") == 0) {
138                                 while(isspace((int) *ptr))
139                                         ptr++;
140
141                                 /*
142                                  *      If it's an absolute pathname,
143                                  *      then use it verbatim.
144                                  *
145                                  *      If not, then make the $include
146                                  *      files *relative* to the current
147                                  *      file.
148                                  */
149                                 if (FR_DIR_IS_RELATIVE(ptr)) {
150                                         char *p;
151
152                                         strlcpy(newfile, file,
153                                                 sizeof(newfile));
154                                         p = strrchr(newfile, FR_DIR_SEP);
155                                         if (!p) {
156                                                 p = newfile + strlen(newfile);
157                                                 *p = FR_DIR_SEP;
158                                         }
159                                         getword(&ptr, p + 1,
160                                                 sizeof(newfile) - 1 - (p - newfile));
161                                 } else {
162                                         getword(&ptr, newfile,
163                                                 sizeof(newfile));
164                                 }
165
166                                 t = NULL;
167                                 
168                                 if (pairlist_read(newfile, &t, 0) != 0) {
169                                         pairlist_free(&pl);
170                                         radlog(L_ERR,
171                                                "%s[%d]: Could not open included file %s: %s",
172                                                file, lineno, newfile, strerror(errno));
173                                         fclose(fp);
174                                         return -1;
175                                 }
176                                 *last = t;
177
178                                 /*
179                                  *      t may be NULL, it may have one
180                                  *      entry, or it may be a linked list
181                                  *      of entries.  Go to the end of the
182                                  *      list.
183                                  */
184                                 while (*last)
185                                         last = &((*last)->next);
186                                 continue;
187                         }
188
189                         /*
190                          *      Parse the check values
191                          */
192                         check_tmp = NULL;
193                         reply_tmp = NULL;
194                         old_lineno = lineno;
195                         parsecode = userparse(ptr, &check_tmp);
196                         if (parsecode == T_OP_INVALID) {
197                                 pairlist_free(&pl);
198                                 radlog(L_ERR,
199                                 "%s[%d]: Parse error (check) for entry %s: %s",
200                                         file, lineno, entry, fr_strerror());
201                                 fclose(fp);
202                                 return -1;
203                         } else if (parsecode == T_COMMA) {
204                                 radlog(L_ERR,
205                                        "%s[%d]: Unexpected trailing comma in check item list for entry %s",
206                                        file, lineno, entry);
207                                 fclose(fp);
208                                 return -1;
209                         }
210                         mode = FIND_MODE_REPLY;
211                         parsecode = T_COMMA;
212                 }
213                 else {
214                         if(*buffer == ' ' || *buffer == '\t') {
215                                 if (parsecode != T_COMMA) {
216                                         radlog(L_ERR,
217                                                "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
218                                                file, lineno, entry);
219                                         fclose(fp);
220                                         return -1;
221                                 }
222
223                                 /*
224                                  *      Parse the reply values
225                                  */
226                                 parsecode = userparse(buffer, &reply_tmp);
227                                 /* valid tokens are 1 or greater */
228                                 if (parsecode < 1) {
229                                         pairlist_free(&pl);
230                                         radlog(L_ERR,
231                                                "%s[%d]: Parse error (reply) for entry %s: %s",
232                                                file, lineno, entry, fr_strerror());
233                                         fclose(fp);
234                                         return -1;
235                                 }
236                         }
237                         else {
238                                 size_t entry_len;
239                                 char *q;
240
241                                 entry_len = strlen(entry) + 1;
242
243                                 /*
244                                  *      Done with this entry...
245                                  */
246                                 q = rad_malloc(sizeof(*t) + entry_len);
247                                 t = (PAIR_LIST *) q;
248
249                                 memset(t, 0, sizeof(*t));
250                                 t->check = check_tmp;
251                                 t->reply = reply_tmp;
252                                 t->lineno = old_lineno;
253                                 check_tmp = NULL;
254                                 reply_tmp = NULL;
255
256                                 q += sizeof(*t);
257                                 memcpy(q, entry, entry_len);
258                                 t->name = q;
259
260                                 *last = t;
261                                 last = &(t->next);
262
263                                 mode = FIND_MODE_NAME;
264                                 if (buffer[0] != 0)
265                                         goto parse_again;
266                         }
267                 }
268         }
269         /*
270          *      Make sure that we also read the last line of the file!
271          */
272         if (mode == FIND_MODE_REPLY) {
273                 buffer[0] = 0;
274                 goto parse_again;
275         }
276         fclose(fp);
277
278         *list = pl;
279         return 0;
280 }
281
282
283 /*
284  *      Debug code.
285  */
286 #if 0
287 static void debug_pair_list(PAIR_LIST *pl)
288 {
289         VALUE_PAIR *vp;
290
291         while(pl) {
292                 printf("Pair list: %s\n", pl->name);
293                 printf("** Check:\n");
294                 for(vp = pl->check; vp; vp = vp->next) {
295                         printf("    ");
296                         fprint_attr_val(stdout, vp);
297                         printf("\n");
298                 }
299                 printf("** Reply:\n");
300                 for(vp = pl->reply; vp; vp = vp->next) {
301                         printf("    ");
302                         fprint_attr_val(stdout, vp);
303                         printf("\n");
304                 }
305                 pl = pl->next;
306         }
307 }
308 #endif