072d08c1218e3bdf17f5d91c24295c220ea5fdf9
[freeradius.git] / src / modules / rlm_dbm / rlm_dbm_parser.c
1 /*
2  * rlm_dbm_parser.c :    Create dbm file from plain text
3  *
4  * Version:     $Id$
5  *
6  *  This program is is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License, version 2 if the
8  *  License as published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  Copyright 2001 Koulik Andrei, Sandy Service
16  */
17
18
19 char sccsid[] =
20 "$Id$ sandy module project\n Copyright 2001 Sandy Service\nCopyright 2001 Koulik Andrei";
21
22 #include "autoconf.h"
23 #include <fcntl.h>
24
25 #include <stdlib.h>
26
27 #ifdef HAVE_NDBM_H
28 #include <ndbm.h>
29 #endif
30
31 #ifdef HAVE_GDBM_NDBM_H
32 #include <gdbm/ndbm.h>
33 #endif
34
35 #ifdef HAVE_GDBMNDBM_H
36 #include <gdbm-ndbm.h>
37 #endif
38
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <string.h>
42
43 #include "conf.h"
44 #include "radpaths.h"
45 #include "missing.h"
46
47 #include "radiusd.h"
48
49 #define MAX_BUFF_SIZE   1024
50
51 #define DOUT1   if( librad_debug > 0 ) printf
52 #define DOUT2   if( librad_debug > 5 ) printf
53
54 typedef enum sm_parse_state_t {
55         SMP_INVALID = 0,
56         SMP_USER,
57         SMP_PATTERN,
58         SMP_ACTION,
59         SMP_PATTERN_OR_USER
60 } sm_parse_state_t;
61
62
63
64
65 const char * progname;
66
67 unsigned long   st_errors = 0,
68                 st_warns  = 0,
69                 st_lines  = 0,
70                 st_users  = 0,
71                 st_skiped = 0,
72                 st_loaded = 0;
73
74
75 /*  test
76
77 int dumplist(VALUE_PAIR *vp) {
78
79         while (vp != NULL) {
80         
81                 printf("VP: name: %s\nattribute: %d\ntype: %d\nlvalue: %lu"
82                         "\noperator %d\naddport: %d\nValue: %s\n",
83                         vp -> name, vp -> attribute, vp -> type, vp -> lvalue,
84                         vp -> operator, vp -> addport, (char*)vp -> strvalue);
85                 vp = vp -> next;        
86         }
87         return 0;
88 }
89
90 */
91
92
93 char const *sm_tokens[]={
94                 "INVALID","EOL","{","}","(",")",",",";","+=",
95                 "-=",":=","=","!=",">=",">","<=","<","=~","!~","==","#"
96            };
97         
98         
99
100 static int sm_prints(char *out, int outlen, VALUE_PAIR *vp) {
101
102         int             len;
103
104         out[0] = 0; 
105         if (!vp) return 0;
106
107         if (strlen(vp->name) + 4 > (size_t)outlen) {
108                 return 0;
109         }
110
111         snprintf(out, outlen, "%s %s ", 
112                         vp->name, 
113                         sm_tokens[vp->operator] );
114         len = strlen(out);
115
116         vp_prints_value(out + len, outlen - len, vp, 1);
117
118
119         return strlen(out);
120         
121 }
122
123
124 char content[4096];
125 int  concntr = 0;
126 int  oflags = O_RDWR | O_CREAT;
127 DBM * pdb = NULL;
128
129
130 static int open_storage(const char * fname) {
131
132   if ( (pdb = dbm_open(fname, oflags, 0600 )) == NULL ) {
133         perror("Couldn't open database");
134         return 1;
135   }
136   return 0; 
137 }
138
139 static void  close_storage(void){
140   dbm_close(pdb); 
141 }
142
143 static int  addlinetocontent(VALUE_PAIR *vp) {
144         
145         int outlen = sizeof(content) - concntr - 1;
146         int lendiv;
147         
148         if ( outlen < 4 ) return -1;
149         if ( vp == NULL ) { /* add empty line */
150                 content[concntr++] = '\n';
151                 content[concntr] = '\0';
152         } else {
153                 while ( vp != NULL ){
154                         lendiv = sm_prints(&content[concntr],outlen,vp);
155                         if ( lendiv > 0 ) {
156                                 outlen -= lendiv;
157                                 
158                                 if (outlen > 3)  {
159                                         strcat(content,", ");
160                                         concntr += lendiv + 2;
161                                         outlen -= 2;
162                                 } else {
163                                         concntr = 0;
164                                         return -1;
165                                 } 
166                         }
167                         vp = vp -> next;        
168                 }
169         
170                 if ( concntr > 2 ) {  /* remove trailing ',' */
171                         content[--concntr] = '\0';
172                         content[concntr - 1] = '\n';
173                 }
174         }
175                 
176         return 0;       
177 }
178
179 static int storecontent (const char * username) {
180         
181          datum d,k;
182          int res;
183         
184         if ( pdb == NULL || concntr < 3 ) return 1;
185
186         DOUT2("store:\n%s\ncontent:\n%s",username,content);
187
188         d.dptr = content;
189         d.dsize = concntr + 1;
190
191         k.dptr = username;
192         k.dsize = strlen(username) + 1;
193         
194         res = dbm_store(pdb, k, d, DBM_INSERT);
195         if ( res == 1 ) dbm_store(pdb, k, d, DBM_REPLACE);
196         if ( res < 0 ) {
197           perror("Couldn't insert record");
198           st_errors++;
199           st_skiped++;
200         }  else st_loaded++;
201         
202         concntr = 0;
203         *content = '\0';
204         return 0;
205 }
206
207 static int getuname(char **p,char *u,int n) {
208         int     i; 
209         
210         for(i=0 ; ( i < n-1 ) && ( **p ) && (! isspace(**p) ) ; (*p)++ ) 
211             u[i++] = **p;
212         u[i] = '\0';
213         return ( i == 0) ? 1:0;
214 }
215
216 static int sm_parse_file(FILE*fp,const char* fname) {
217         LRAD_TOKEN tok;
218         VALUE_PAIR *vp = NULL;
219         sm_parse_state_t  parse_state = SMP_USER; 
220         unsigned long lino  = 0;
221         char *p;
222         char buff[MAX_BUFF_SIZE];
223         char username[256];
224
225
226         while( parse_state != SMP_INVALID && fgets(buff, sizeof(buff), fp) != NULL ) {
227                 
228                 lino ++;
229                 st_lines++;
230                 if ( strchr(buff, '\n') == NULL) {
231                         fprintf(stderr,"%s: %s[%lu]:Warning: line too long or not closed by \\n character. Skiped\n",progname,fname,lino);
232                         st_warns++;
233                         st_skiped++; /* _LINE_ skiped */
234                         continue;
235                 }
236         
237                 DOUT2("Parseline: %s",buff);
238                 for ( p = buff; isspace(*p); p++);
239                  
240                 if ( *p == '#' || *p == 0 ) continue;
241
242                 /* userparse hack */
243                 if (  *p == ';' ) *p = '\n';
244                 p = buff;
245
246                 /* try to decide is this line new user or new pattern */
247                 if ( parse_state == SMP_PATTERN_OR_USER ) {
248                      if ( isspace(buff[0]) ) parse_state = SMP_PATTERN;
249                         else {
250                                 parse_state = SMP_USER;
251                                 storecontent(username);
252                                 st_users++;
253                         }
254                  }
255
256                 if ( parse_state == SMP_USER ) {
257                     tok = getuname(&p,username,sizeof(username));
258                     
259                     /* check: is it include. not implemented */
260
261                     if ( tok ) {
262                         fprintf(stderr ,"%s: %s[%lu]: error while expecting user name\n",progname,fname,lino);
263                         parse_state = SMP_INVALID;
264                         st_errors++;
265                     } else {
266                         parse_state = SMP_PATTERN;
267                         DOUT1("Found user: %s\n",username);
268                         
269                     }
270                 }
271                 if ( parse_state == SMP_PATTERN || parse_state == SMP_ACTION ) {
272
273                     /* check for empty line */
274                     while( *p && isspace(*p) ) p++;
275
276                     if ( *p && ( *p != ';' ) ) tok = userparse(p,&vp);
277                     else tok = T_EOL;  /* ';' - signs empty line */
278                     
279                     switch(tok) {
280                         case T_EOL: /* add to content */
281                                         addlinetocontent(vp);
282                                         pairfree(&vp);
283                                         if ( parse_state == SMP_PATTERN ) 
284                                                 parse_state = SMP_ACTION;
285                                         else parse_state = SMP_PATTERN_OR_USER;
286                                          
287                         case T_COMMA: break;  /* parse next line */
288                         default: /* error: we do  not expect anything else */
289                                         fprintf(stderr ,"%s: %s[%lu]: sintax error\n",progname,fname,lino); 
290                                         librad_perror("Error");
291                                         parse_state = SMP_INVALID;
292                                         st_errors++;
293                     }   
294                 }
295         }
296         if ( feof(fp) ) switch (parse_state ) {
297                 case  SMP_USER: /* file is empty, last line is comment  */
298                                                 break;
299                 case  SMP_PATTERN: /* only username ?*/
300                                 fprintf(stderr ,"%s: %s[%lu]: EOF while pattern line are expecting\n",progname,fname,lino);
301                                 st_errors++;
302                                 parse_state = SMP_INVALID;
303                                 break;
304                 case  SMP_ACTION: /* looking for reply line */
305                                 fprintf(stderr ,"%s: %s[%lu]: EOF while reply line are expecting\n",progname,fname,lino);
306                                 st_errors++;
307                                 parse_state = SMP_INVALID;
308                                 break;
309                 case  SMP_PATTERN_OR_USER:
310                                 storecontent(username);
311                                 st_users++;
312                                 break;
313                 default:break;
314         } else if ( parse_state != SMP_INVALID ) {  /* file read error */
315                 fprintf(stderr ,"%s: error file reading from file\n",progname);
316         }
317         pairfree(&vp);
318
319         return (parse_state == SMP_INVALID)?-1:0;
320 }
321
322
323 static void sm_usage(void) {
324         fprintf(stderr, "Usage: %s [-c] [-d raddb] [-f imputfile] [-o outputfile] [-x] [-v] [-q] [username1 [username2] ...]\n\n",progname);
325
326         fprintf(stderr, "-c     create new database.\n");
327         fprintf(stderr, "-x     debug mode.\n");
328         fprintf(stderr, "-q     do not print statistic\n");
329         fprintf(stderr, "-v     print version\n");
330         fprintf(stderr, "-r     remove user(s) from database\n");
331         
332 }
333
334 int main(int n,char **argv) {
335         
336         const char *fname = NULL;
337         const char *ofile = NULL;
338         FILE    *fp;
339         int     print_stat = 1;
340         int     ch;
341         const char  *sm_radius_dir = NULL;
342         
343         progname = argv[0];
344
345         librad_debug = 0;
346         
347         while ((ch = getopt(n, argv, "d:i:xo:qvc")) != -1) 
348                 switch (ch) {
349                         case 'd':       
350                                 sm_radius_dir = optarg;
351                                 break;
352                         case 'i':
353                                 fname = optarg;
354                                 break;
355                         case 'x':
356                                 librad_debug++;
357                         case 'o':
358                                 ofile = optarg;
359                                 break;  
360                         case 'q':
361                                 print_stat = 0;
362                                 break;
363                         case 'v':
364                                 printf("%s: $Id$ \n",progname);
365                                 exit(0);
366                         case 'c':
367                                 oflags = O_CREAT | O_TRUNC | O_RDWR;
368                                 break;
369                         default: sm_usage();exit(1);
370                 }
371
372
373
374
375         if ( sm_radius_dir == NULL ) sm_radius_dir = RADDBDIR;
376         
377         DOUT1("Use dictionary in: %s\n",sm_radius_dir);
378         if (dict_init(sm_radius_dir, RADIUS_DICTIONARY) < 0 ) {
379                 librad_perror("parser: init dictionary:");
380                 exit(1);
381         }
382
383         if ( fname == NULL || fname[0] == '-') {
384                 fp = stdin;
385                 fname = "STDIN";
386         } else if ( ( fp = fopen(fname, "r") ) == NULL ) {
387                 fprintf( stderr,"%s: Couldn't open source file\n", progname);
388                 exit(1);
389         } 
390         
391         if ( ofile == NULL ) ofile = "sandy_db" ;
392         if ( open_storage(ofile) ) {
393                 exit (1);
394         }
395
396         sm_parse_file(fp,fname);
397
398         close_storage();
399
400         if ( print_stat )
401           fprintf(stderr,"\nRecord loaded: %lu\nLines parsed: %lu\nRecord skiped: %lu\nWarnings: %lu\nErrors: %lu\n"
402                 ,st_loaded,st_lines,st_skiped,st_warns,st_errors);
403
404         return 0;
405 }