Fix typo
[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         /*
78          *      Open the file.  The error message should be a little
79          *      more useful...
80          */
81         if ((fp = fopen(file, "r")) == NULL) {
82                 if (!complain)
83                         return -1;
84                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
85                                 file, strerror(errno));
86                 return -1;
87         }
88
89         parsecode = T_EOL;
90
91         /*
92          *      Read the entire file into memory for speed.
93          */
94         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
95                 lineno++;
96                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
97                         fclose(fp);
98                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
99                         pairlist_free(&pl);
100                         return -1;
101                 }
102                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
103
104                 /*
105                  *      If the line contains nothing but whitespace,
106                  *      ignore it.
107                  */
108                 ptr = buffer;
109                 while (isspace((int) *ptr)) ptr++;
110                 if (*ptr == '\0') continue;
111
112 parse_again:
113                 if(mode == FIND_MODE_NAME) {
114                         /*
115                          *      Find the entry starting with the users name
116                          */
117                         if (isspace((int) buffer[0]))  {
118                                 if (parsecode != T_EOL) {
119                                         radlog(L_ERR|L_CONS,
120                                                "%s[%d]: Unexpected trailing comma for entry %s",
121                                                file, lineno, entry);
122                                         fclose(fp);
123                                         return -1;
124                                 }
125                                 continue;
126                         }
127
128                         ptr = buffer;
129                         getword(&ptr, entry, sizeof(entry));
130
131                         /*
132                          *      Include another file if we see
133                          *      $INCLUDE filename
134                          */
135                         if (strcasecmp(entry, "$include") == 0) {
136                                 while(isspace((int) *ptr))
137                                         ptr++;
138
139                                 /*
140                                  *      If it's an absolute pathname,
141                                  *      then use it verbatim.
142                                  *
143                                  *      If not, then make the $include
144                                  *      files *relative* to the current
145                                  *      file.
146                                  */
147                                 if (FR_DIR_IS_RELATIVE(ptr)) {
148                                         char *p;
149
150                                         strlcpy(newfile, file,
151                                                 sizeof(newfile));
152                                         p = strrchr(newfile, FR_DIR_SEP);
153                                         if (!p) {
154                                                 p = newfile + strlen(newfile);
155                                                 *p = FR_DIR_SEP;
156                                         }
157                                         getword(&ptr, p + 1,
158                                                 sizeof(newfile) - 1 - (p - buffer));
159                                 } else {
160                                         getword(&ptr, newfile,
161                                                 sizeof(newfile));
162                                 }
163
164                                 t = NULL;
165                                 if (pairlist_read(newfile, &t, 0) != 0) {
166                                         pairlist_free(&pl);
167                                         radlog(L_ERR|L_CONS,
168                                                "%s[%d]: Could not open included file %s: %s",
169                                                file, lineno, newfile, strerror(errno));
170                                         fclose(fp);
171                                         return -1;
172                                 }
173                                 *last = t;
174
175                                 /*
176                                  *      t may be NULL, it may have one
177                                  *      entry, or it may be a linked list
178                                  *      of entries.  Go to the end of the
179                                  *      list.
180                                  */
181                                 while (*last)
182                                         last = &((*last)->next);
183                                 continue;
184                         }
185
186                         /*
187                          *      Parse the check values
188                          */
189                         check_tmp = NULL;
190                         reply_tmp = NULL;
191                         old_lineno = lineno;
192                         parsecode = userparse(ptr, &check_tmp);
193                         if (parsecode == T_OP_INVALID) {
194                                 pairlist_free(&pl);
195                                 radlog(L_ERR|L_CONS,
196                                 "%s[%d]: Parse error (check) for entry %s: %s",
197                                         file, lineno, entry, fr_strerror());
198                                 fclose(fp);
199                                 return -1;
200                         } else if (parsecode == T_COMMA) {
201                                 radlog(L_ERR|L_CONS,
202                                        "%s[%d]: Unexpected trailing comma in check item list for entry %s",
203                                        file, lineno, entry);
204                                 fclose(fp);
205                                 return -1;
206                         }
207                         mode = FIND_MODE_REPLY;
208                         parsecode = T_COMMA;
209                 }
210                 else {
211                         if(*buffer == ' ' || *buffer == '\t') {
212                                 if (parsecode != T_COMMA) {
213                                         radlog(L_ERR|L_CONS,
214                                                "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
215                                                file, lineno, entry);
216                                         fclose(fp);
217                                         return -1;
218                                 }
219
220                                 /*
221                                  *      Parse the reply values
222                                  */
223                                 parsecode = userparse(buffer, &reply_tmp);
224                                 /* valid tokens are 1 or greater */
225                                 if (parsecode < 1) {
226                                         pairlist_free(&pl);
227                                         radlog(L_ERR|L_CONS,
228                                                "%s[%d]: Parse error (reply) for entry %s: %s",
229                                                file, lineno, entry, fr_strerror());
230                                         fclose(fp);
231                                         return -1;
232                                 }
233                         }
234                         else {
235                                 size_t entry_len;
236                                 char *q;
237
238                                 entry_len = strlen(entry) + 1;
239
240                                 /*
241                                  *      Done with this entry...
242                                  */
243                                 q = rad_malloc(sizeof(*t) + entry_len);
244                                 t = (PAIR_LIST *) q;
245
246                                 memset(t, 0, sizeof(*t));
247                                 t->check = check_tmp;
248                                 t->reply = reply_tmp;
249                                 t->lineno = old_lineno;
250                                 check_tmp = NULL;
251                                 reply_tmp = NULL;
252
253                                 q += sizeof(*t);
254                                 memcpy(q, entry, entry_len);
255                                 t->name = q;
256
257                                 *last = t;
258                                 last = &(t->next);
259
260                                 mode = FIND_MODE_NAME;
261                                 if (buffer[0] != 0)
262                                         goto parse_again;
263                         }
264                 }
265         }
266         /*
267          *      Make sure that we also read the last line of the file!
268          */
269         if (mode == FIND_MODE_REPLY) {
270                 buffer[0] = 0;
271                 goto parse_again;
272         }
273         fclose(fp);
274
275         *list = pl;
276         return 0;
277 }
278
279
280 /*
281  *      Debug code.
282  */
283 #if 0
284 static void debug_pair_list(PAIR_LIST *pl)
285 {
286         VALUE_PAIR *vp;
287
288         while(pl) {
289                 printf("Pair list: %s\n", pl->name);
290                 printf("** Check:\n");
291                 for(vp = pl->check; vp; vp = vp->next) {
292                         printf("    ");
293                         fprint_attr_val(stdout, vp);
294                         printf("\n");
295                 }
296                 printf("** Reply:\n");
297                 for(vp = pl->reply; vp; vp = vp->next) {
298                         printf("    ");
299                         fprint_attr_val(stdout, vp);
300                         printf("\n");
301                 }
302                 pl = pl->next;
303         }
304 }
305 #endif