1/*
2  By accepting this notice, you agree to be bound by the following
3  agreements:
4
5  This software product, squidGuard, is copyrighted (C) 1998-2008
6  by Christine Kronberg, Shalla Secure Services. All rights reserved.
7
8  This program is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License (version 2) as
10  published by the Free Software Foundation.  It is distributed in the
11  hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
12  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13  PURPOSE.  See the GNU General Public License (GPL) for more details.
14
15  You should have received a copy of the GNU General Public License
16  (GPL) along with this program.
17*/
18
19
20%{
21#include "sg.h"
22extern int globalDebug;
23
24#ifdef HAVE_LIBLDAP
25#include "lber.h"
26#include "ldap.h"
27#endif
28
29#ifdef HAVE_MYSQL
30#include <mysql.h>
31#endif
32
33#include "sgEx.h"
34
35FILE *yyin, *yyout;
36char *configFile;
37
38int numTimeElements;
39int *TimeElementsEvents;
40
41static int time_switch = 0;
42static int date_switch = 0;
43
44int numSource = 0;
45
46void rfc1738_unescape(char *);
47void
48rfc1738_unescape(char *s)
49{
50    char hexnum[3];
51    int i, j;                   /* i is write, j is read */
52    unsigned int x;
53    for (i = j = 0; s[j]; i++, j++) {
54        s[i] = s[j];
55        if (s[i] != '%')
56            continue;
57        if (s[j + 1] == '%') {  /* %% case */
58            j++;
59            continue;
60        }
61        if (s[j + 1] && s[j + 2]) {
62            if (s[j + 1] == '0' && s[j + 2] == '0') {   /* %00 case */
63                j += 2;
64                continue;
65            }
66            hexnum[0] = s[j + 1];
67            hexnum[1] = s[j + 2];
68            hexnum[2] = '\0';
69            if (1 == sscanf(hexnum, "%x", &x)) {
70                s[i] = (char) (0x0ff & x);
71                j += 2;
72            }
73        }
74    }
75    s[i] = '\0';
76}
77
78%}
79
80%union {
81  char *string;
82  char *tval;
83  char *dval;
84  char *dvalcron;
85  int  *integer;
86}
87
88%token WORD END START_BRACKET STOP_BRACKET WEEKDAY LDAPDNSTR
89%token DESTINATION REWRITE ACL TIME TVAL DVAL DVALCRON
90%token SOURCE CIDR IPCLASS CONTINUE
91%token IPADDR DBHOME DOMAINLIST URLLIST EXPRESSIONLIST IPLIST
92%token DOMAIN USER USERLIST USERQUERY LDAPUSERSEARCH USERQUOTA IP NL NUMBER
93%token PASS REDIRECT LOGDIR SUBST CHAR MINUTELY HOURLY DAILY WEEKLY DATE
94%token WITHIN OUTSIDE ELSE LOGFILE ANONYMOUS VERBOSE CONTINIOUS SPORADIC
95%token LDAPCACHETIME EXECUSERLIST EXECCMD LDAPPROTOVER
96%token LDAPBINDDN LDAPBINDPASS MYSQLUSERNAME MYSQLPASSWORD DATABASE
97
98%type <string> WORD
99%type <string> EXECCMD
100%type <string> WEEKDAY
101%type <string> LDAPDNSTR
102%type <string> NUMBER
103%type <tval> TVAL
104%type <string> DVAL
105%type <string> DVALCRON
106%type <string> CHAR
107%type <string> SUBST
108%type <string> IPADDR
109%type <string> DBHOME LOGDIR
110%type <string> CIDR
111%type <string> IPCLASS
112%type <string> acl_content
113%type <string> acl
114%type <string> dval
115%type <string> dvalcron
116%type <string> tval
117%type <string> date
118%type <string> ttime
119%%
120
121start: statements
122       ;
123
124dbhome:    DBHOME WORD { sgSetting("dbhome",$2); }
125         ;
126
127logdir:    LOGDIR WORD { sgSetting("logdir",$2); }
128         ;
129
130ldapcachetime: LDAPCACHETIME NUMBER { sgSetting("ldapcachetime",$2); }
131         ;
132
133ldapprotover: LDAPPROTOVER NUMBER {sgSetting("ldapprotover",$2); }
134       ;
135
136ldapbinddn: LDAPBINDDN LDAPDNSTR { sgSetting("ldapbinddn",$2); }
137       ;
138
139ldapbindpass: LDAPBINDPASS WORD { sgSetting("ldapbindpass",$2); }
140       ;
141
142mysqlusername: MYSQLUSERNAME WORD { sgSetting("mysqlusername",$2); }
143       ;
144
145mysqlpassword: MYSQLPASSWORD WORD { sgSetting("mysqlpassword",$2); }
146       ;
147
148mysqldb:       DATABASE WORD { sgSetting("mysqldb",$2); }
149       ;
150
151start_block:
152               START_BRACKET
153	       ;
154
155stop_block:
156               STOP_BRACKET
157	       ;
158
159destination: DESTINATION WORD { sgDest($2); }
160             ;
161
162destination_block: destination start_block destination_contents stop_block
163                       { sgDestEnd();}
164                ;
165
166destination_contents:
167                  | destination_contents destination_content
168		  ;
169destination_content:
170 	    DOMAINLIST WORD { sgDestDomainList($2); }
171            | DOMAINLIST '-' { sgDestDomainList(NULL); }
172            | URLLIST WORD { sgDestUrlList($2); }
173            | URLLIST '-'  { sgDestUrlList(NULL); }
174            | EXPRESSIONLIST '-' { sgDestExpressionList(NULL,NULL); }
175            | EXPRESSIONLIST 'i' WORD { sgDestExpressionList($3,"i"); }
176            | EXPRESSIONLIST WORD  { sgDestExpressionList($2,"n"); }
177            | REDIRECT WORD  {sgDestRedirect($2); }
178            | REWRITE WORD  {sgDestRewrite($2); }
179            | WITHIN WORD { sgDestTime($2,WITHIN); }
180            | OUTSIDE WORD { sgDestTime($2,OUTSIDE); }
181            | LOGFILE ANONYMOUS WORD { sgLogFile(SG_BLOCK_DESTINATION,1,0,$3); }
182            | LOGFILE VERBOSE WORD { sgLogFile(SG_BLOCK_DESTINATION,0,1,$3); }
183            | LOGFILE ANONYMOUS VERBOSE WORD { sgLogFile(SG_BLOCK_DESTINATION,1,1,$4); }
184            | LOGFILE VERBOSE ANONYMOUS WORD { sgLogFile(SG_BLOCK_DESTINATION,1,1,$4); }
185            | LOGFILE WORD { sgLogFile(SG_BLOCK_DESTINATION,0,0,$2); }
186            ;
187
188source:      SOURCE WORD { sgSource($2); }
189             ;
190
191source_block: source start_block source_contents stop_block {sgSourceEnd();}
192             ;
193
194source_contents:
195		    | source_contents source_content
196		    ;
197
198source_content:     DOMAIN domain
199                    | USER user
200                    | USERLIST WORD { sgSourceUserList($2); }
201@MYSQLLINE@
202@YACCLINE@
203                    | EXECUSERLIST EXECCMD { sgSourceExecUserList($2); }
204                    | USERQUOTA NUMBER NUMBER HOURLY { sgSourceUserQuota($2,$3,"3600");}
205                    | USERQUOTA NUMBER NUMBER DAILY { sgSourceUserQuota($2,$3,"86400");}
206                    | USERQUOTA NUMBER NUMBER WEEKLY { sgSourceUserQuota($2,$3,"604800");}
207                    | USERQUOTA NUMBER NUMBER NUMBER { sgSourceUserQuota($2,$3,$4);}
208                    | IP ips
209                    | IPLIST WORD { sgSourceIpList($2); }
210                    | WITHIN WORD { sgSourceTime($2,WITHIN); }
211                    | OUTSIDE WORD { sgSourceTime($2,OUTSIDE); }
212                    | LOGFILE ANONYMOUS WORD {sgLogFile(SG_BLOCK_SOURCE,1,0,$3);}
213                    | LOGFILE VERBOSE WORD {sgLogFile(SG_BLOCK_SOURCE,0,1,$3);}
214                    | LOGFILE ANONYMOUS VERBOSE WORD {sgLogFile(SG_BLOCK_SOURCE,1,1,$4);}
215                    | LOGFILE VERBOSE ANONYMOUS WORD {sgLogFile(SG_BLOCK_SOURCE,1,1,$4);}
216                    | LOGFILE WORD { sgLogFile(SG_BLOCK_SOURCE,0,0,$2); }
217                    | CONTINUE { lastSource->cont_search = 1; }
218                    ;
219domain:
220		    | domain WORD { sgSourceDomain($2); }
221                    | domain ','
222		    ;
223
224user:
225		    | user WORD { sgSourceUser($2); }
226                    | user ','
227		    ;
228
229acl_block: ACL start_block acl_contents stop_block
230             ;
231
232acl_contents:
233		    | acl_contents acl_content
234		    ;
235
236acl:            WORD {sgAcl($1,NULL,0);}
237               | WORD WITHIN WORD {sgAcl($1,$3,WITHIN);}
238               | WORD OUTSIDE WORD { sgAcl($1,$3,OUTSIDE); }
239                ;
240
241acl_content:     acl start_block access_contents stop_block
242                 | acl start_block access_contents stop_block ELSE
243                   {sgAcl(NULL,NULL,ELSE);}
244                       start_block access_contents stop_block
245                 ;
246
247access_contents:
248                   | access_contents access_content
249                   ;
250
251access_content:    PASS access_pass { }
252                  | REWRITE WORD { sgAclSetValue("rewrite",$2,0); }
253                  | REDIRECT WORD { sgAclSetValue("redirect",$2,0); }
254                  | LOGFILE ANONYMOUS WORD {sgLogFile(SG_BLOCK_ACL,1,0,$3);}
255                  | LOGFILE VERBOSE WORD {sgLogFile(SG_BLOCK_ACL,0,1,$3);}
256                  | LOGFILE ANONYMOUS VERBOSE WORD {sgLogFile(SG_BLOCK_ACL,1,1,$4);}
257                  | LOGFILE VERBOSE ANONYMOUS WORD {sgLogFile(SG_BLOCK_ACL,1,1,$4);}
258                  | LOGFILE WORD { sgLogFile(SG_BLOCK_ACL,0,0,$2); }
259                  ;
260
261access_pass:
262                  | access_pass WORD { sgAclSetValue("pass",$2,1);}
263                  | access_pass '!' WORD { sgAclSetValue("pass",$3,0);}
264		  | access_pass ','
265                  ;
266
267cidr:             CIDR { sgIp($1); }
268                  ;
269
270ipclass:          IPCLASS { sgIp($1); }
271                  ;
272ips:
273                    | ips ip { sgIp("255.255.255.255") ; sgSetIpType(SG_IPTYPE_HOST,NULL,0); }
274                    | ips ip cidr { sgSetIpType(SG_IPTYPE_CIDR,NULL,0); }
275                    | ips ip ipclass { sgSetIpType(SG_IPTYPE_CLASS,NULL,0); }
276                    | ips ip '-' ip  { sgSetIpType(SG_IPTYPE_RANGE,NULL,0); }
277                    | ips ','
278		    ;
279
280ip:  IPADDR { sgIp($1);}
281     ;
282
283rew:       REWRITE WORD { sgRewrite($2); }
284             ;
285
286rew_block:  rew start_block rew_contents stop_block
287             ;
288
289rew_contents:
290		    | rew_contents rew_content
291		    ;
292
293
294rew_content:    SUBST  { sgRewriteSubstitute($1); }
295                | WITHIN WORD { sgRewriteTime($2,WITHIN); }
296                | OUTSIDE WORD { sgRewriteTime($2,OUTSIDE); }
297                | LOGFILE ANONYMOUS WORD { sgLogFile(SG_BLOCK_REWRITE,1,0,$3); }
298                | LOGFILE VERBOSE WORD { sgLogFile(SG_BLOCK_REWRITE,0,1,$3); }
299                | LOGFILE ANONYMOUS VERBOSE WORD { sgLogFile(SG_BLOCK_REWRITE,1,1,$4); }
300                | LOGFILE VERBOSE ANONYMOUS WORD { sgLogFile(SG_BLOCK_REWRITE,1,1,$4); }
301                | LOGFILE WORD { sgLogFile(SG_BLOCK_REWRITE,0,0,$2); }
302                ;
303
304
305time:       TIME WORD { sgTime($2); }
306             ;
307
308time_block:  time start_block time_contents stop_block
309             ;
310
311time_contents:
312		    | time_contents time_content
313		    ;
314
315
316time_content:    WEEKLY {sgTimeElementInit();} WORD
317                         {sgTimeElementAdd($3,T_WEEKLY);} ttime
318                 | WEEKLY {sgTimeElementInit();} WEEKDAY
319                         {sgTimeElementAdd($3,T_WEEKDAY);} ttime
320                 | DATE {sgTimeElementInit();} date
321                         {sgTimeElementEnd();}
322                 ;
323
324ttime:           ttime { sgTimeElementClone(); } tval '-' tval
325		 | tval '-' tval
326                 ;
327
328date:            dval ttime
329                 | dval
330                 | dval '-' dval ttime
331                 | dval '-' dval
332                 | dvalcron ttime
333                 | dvalcron
334                 ;
335
336dval:		 DVAL { sgTimeElementAdd($1,T_DVAL);}
337                 ;
338
339tval:		 TVAL { sgTimeElementAdd($1,T_TVAL);}
340                 ;
341
342dvalcron:	 DVALCRON { sgTimeElementAdd($1,T_DVALCRON);}
343                 ;
344
345statements:
346       | statements statement
347       ;
348
349statement:
350             destination
351	     | source_block
352	     | destination_block
353             | dbhome
354	     | logdir
355            | ldapprotover
356            | ldapbinddn
357            | ldapbindpass
358             | ldapcachetime
359	     | mysqlusername
360	     | mysqlpassword
361	     | mysqldb
362	     | acl_block
363	     | rew_block
364	     | time_block
365	     | NL
366             ;
367
368%%
369
370#if __STDC__
371void sgReadConfig (char *file)
372#else
373void sgReadConfig (file)
374     char *file;
375#endif
376{
377  char *defaultFile=DEFAULT_CONFIGFILE;
378  lineno = 1;
379  configFile = file;
380  if(configFile == NULL)
381    configFile = defaultFile;
382  yyin = fopen(configFile,"r");
383  if(yyin == NULL)
384    sgLogFatalError("%s: can't open configfile  %s",progname, configFile);
385  (void)yyparse();
386  if(defaultAcl == NULL)
387    sgLogFatalError("%s: default acl not defined in configfile  %s",
388	progname, configFile);
389  fclose(yyin);
390}
391
392
393/*
394
395  Logfile functions
396
397*/
398
399#if __STDC__
400void sgLogFile (int block, int anonymous, int verbose, char *file)
401#else
402void sgLogFile (block, anonymous, verbose, file)
403     int block;
404     int anonymous;
405     int verbose;
406     char *file;
407#endif
408{
409  void **v;
410  char *name;
411  struct LogFile *p;
412  switch(block){
413  case(SG_BLOCK_DESTINATION):
414    v = (void **) &lastDest->logfile;
415    name = lastDest->name;
416    break;
417  case(SG_BLOCK_SOURCE):
418    v = (void **) &lastSource->logfile;
419    name = lastSource->name;
420    break;
421  case(SG_BLOCK_REWRITE):
422    v = (void **) &lastRewrite->logfile;
423    name = lastRewrite->name;
424    break;
425  case(SG_BLOCK_ACL):
426    v = (void **) &lastAcl->logfile;
427    name = lastAcl->name;
428    if(strcmp(name,"default")){
429      sgLogError("logfile not allowed in acl other than default");
430    }
431    break;
432  default:
433    return;
434  }
435  if(*v == NULL){
436    p = (struct LogFile *) sgCalloc(1,sizeof(struct LogFile));
437    p->stat = sgLogFileStat(file);
438    p->parent_name = name;
439    p->parent_type = block;
440    p->anonymous = anonymous;
441    p->verbose = verbose;
442    *v = p;
443  } else {
444    sgLogError("%s: redefine of logfile %s in line %d",
445		    progname,file,lineno);
446    return;
447  }
448}
449
450#if __STDC__
451struct LogFileStat *sgLogFileStat(char *file)
452#else
453struct LogFileStat *sgLogFileStat(file)
454     char *file;
455#endif
456{
457  struct LogFileStat *sg;
458  struct stat s;
459  char buf[MAX_BUF];
460  FILE *fd;
461  strncpy(buf,file,MAX_BUF);
462  if(*file != '/'){
463    if(globalLogDir == NULL)
464      strncpy(buf,DEFAULT_LOGDIR,MAX_BUF);
465    else
466      strncpy(buf,globalLogDir,MAX_BUF);
467    strcat(buf,"/");
468    strcat(buf,file);
469  }
470  if((fd = fopen(buf, "a")) == NULL){
471    sgLogError("%s: can't write to logfile %s",progname,buf);
472    return NULL;
473  }
474  if(stat(buf,&s) != 0){
475    sgLogError("%s: can't stat logfile %s",progname,buf);
476    return NULL;
477  }
478  if(LogFileStat == NULL){
479    sg = (struct LogFileStat *) sgCalloc(1,sizeof(struct LogFileStat));
480    sg->name = sgMalloc(strlen(buf) + 1);
481    strcpy(sg->name,buf);
482    sg->st_ino = s.st_ino;
483    sg->st_dev = s.st_dev;
484    sg->fd = fd;
485    sg->next = NULL;
486    LogFileStat = sg;
487    lastLogFileStat = sg;
488  } else {
489    for(sg = LogFileStat; sg != NULL; sg = sg->next){
490      if(sg->st_ino == s.st_ino && sg->st_dev == s.st_dev){
491	fclose(fd);
492	return sg;
493      }
494    }
495    sg = (struct LogFileStat *) sgCalloc(1,sizeof(struct LogFileStat));
496    sg->name = sgMalloc(strlen(buf) + 1);
497    strcpy(sg->name,buf);
498    sg->st_ino = s.st_ino;
499    sg->st_dev = s.st_dev;
500    sg->fd = fd;
501    sg->next = NULL;
502    lastLogFileStat->next = sg;
503    lastLogFileStat = sg;
504  }
505  return lastLogFileStat;
506}
507/*
508
509  Source functions
510
511*/
512
513#if __STDC__
514void sgSource(char *source)
515#else
516void sgSource(source)
517     char *source;
518#endif
519{
520  struct Source *sp;
521  if(Source != NULL){
522    if((struct Source *) sgSourceFindName(source) != NULL)
523      sgLogFatalError("%s: source %s is defined in configfile %s",
524		      progname,source, configFile);
525  }
526  sp = (struct Source *)sgCalloc(1,sizeof(struct Source));
527  sp->ip=NULL;
528  sp->userDb=NULL;
529  sp->domainDb=NULL;
530  sp->active = 1;
531  sp->within = 0;
532  sp->cont_search = 0;
533  sp->time = NULL;
534  sp->userquota.seconds = 0;
535  sp->userquota.renew = 0;
536  sp->userquota.sporadic = 0;
537  sp->next=NULL;
538  sp->logfile = NULL;
539  sp->name = (char  *) sgCalloc(1,strlen(source) + 1);
540  strcpy(sp->name,source);
541
542  if(Source == NULL){
543    Source = sp;
544    lastSource = sp;
545  } else {
546    lastSource->next = sp;
547    lastSource = sp;
548  }
549}
550
551#ifdef HAVE_LIBLDAP
552void sgSourceEnd()
553{
554 struct Source *s;
555 s = lastSource;
556 if(s->ip == NULL && s->domainDb == NULL && s->userDb == NULL
557       && s->ldapurlcount == 0){
558   sgLogError("sourceblock %s missing active content, set inactive",s->name);
559   s->time = NULL;
560   s->active = 0;
561 }
562}
563#else
564void sgSourceEnd()
565{
566 struct Source *s;
567 s = lastSource;
568 if(s->ip == NULL && s->domainDb == NULL && s->userDb == NULL){
569   sgLogError("sourceblock %s missing active content, set inactive",s->name);
570   s->time = NULL;
571   s->active = 0;
572 }
573}
574#endif
575
576#if __STDC__
577void sgSourceUser(char *user)
578#else
579void sgSourceUser(user)
580     char *user;
581#endif
582{
583  struct Source *sp;
584  char *lc;
585  sp = lastSource;
586  if(sp->userDb == NULL){
587    sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
588    sp->userDb->type=SGDBTYPE_USERLIST;
589    sgDbInit(sp->userDb,NULL);
590  }
591  for(lc=user; *lc != '\0'; lc++) /* convert username to lowercase chars */
592    *lc = tolower(*lc);
593  sgDbUpdate(sp->userDb, user, (char *) setuserinfo(),
594            sizeof(struct UserInfo));
595// DEBUG
596  sgLogError("Added User: %s", user);
597}
598
599#if __STDC__
600void sgSourceUserList(char *file)
601#else
602void sgSourceUserList(file)
603     char *file;
604#endif
605{
606  char *dbhome = NULL, *f;
607  FILE *fd;
608  char line[MAX_BUF];
609  char *p,*c,*s,*lc;
610  int l=0;
611  struct Source *sp;
612  sp = lastSource;
613  if(sp->userDb == NULL){
614    sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
615    sp->userDb->type=SGDBTYPE_USERLIST;
616    sgDbInit(sp->userDb,NULL);
617  }
618  dbhome = sgSettingGetValue("dbhome");
619  if(dbhome == NULL)
620    dbhome = DEFAULT_DBHOME;
621  if (file[0] == '/') {
622    f = strdup(file);
623  } else {
624    f = (char  *) sgCalloc(1,strlen(dbhome) + strlen(file) + 5);
625    strcpy(f,dbhome);
626    strcat(f,"/");
627    strcat(f,file);
628  }
629  if((fd = fopen(f,"r")) == NULL){
630    sgLogError("%s: can't open userlist %s: %s",progname, f,strerror(errno));
631    return;
632  }
633  while(fgets(line,sizeof(line),fd) != NULL){
634    l++;
635    if(*line == '#')
636      continue;
637    p = strchr(line,'\n');
638    if(p != NULL && p != line){
639      if(*(p - 1) == '\r') /* removing ^M  */
640	p--;
641      *p = '\0';
642    }
643    c = strchr(line,'#');
644    p = strtok(line," \t,");
645    if((s = strchr(line,':')) != NULL){
646      *s = '\0';
647      for(lc=line; *lc != '\0'; lc++) /* convert username to lowercase chars */
648	*lc = tolower(*lc);
649      sgDbUpdate(sp->userDb, line, (char *) setuserinfo(),
650                sizeof(struct UserInfo));
651    } else {
652      do {
653	if(c != NULL && p >= c) /*find the comment */
654	  break;
655	for(lc=p; *lc != '\0'; lc++) /* convert username to lowercase chars */
656	  *lc = tolower(*lc);
657       sgDbUpdate(sp->userDb, p, (char *) setuserinfo(),
658                  sizeof(struct UserInfo));
659// DEBUG
660        sgLogError("Added UserList source: %s", p);
661      } while((p=strtok(NULL," \t,")) != NULL);
662    }
663  }
664  fclose(fd);
665}
666
667
668/* MySQLsupport */
669#ifdef HAVE_MYSQL
670#if __STDC__
671void sgSourceUserQuery(char *query)
672#else
673void sgSourceUserQuery(query)
674     char *query;
675#endif
676{
677  char *dbhome = NULL, *f;
678  MYSQL *conn;
679  MYSQL_RES *res;
680  MYSQL_ROW *row;
681  char line[MAX_BUF];
682  char *my_query, *my_user, *my_pass, *my_db;
683  char *str=";";
684  int l=0;
685  struct Source *sp;
686  sp = lastSource;
687  if(sp->userDb == NULL){
688    sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
689    sp->userDb->type=SGDBTYPE_USERLIST;
690    sgDbInit(sp->userDb,NULL);
691  }
692  dbhome = sgSettingGetValue("dbhome");
693  my_user = sgSettingGetValue("mysqlusername");
694  my_pass = sgSettingGetValue("mysqlpassword");
695  my_db = sgSettingGetValue("mysqldb");
696  if(dbhome == NULL) {
697    dbhome = DEFAULT_DBHOME;
698  }
699  if( !(conn = mysql_init(0)) ) {
700    @NOLOG1@ sgLogError("%s: can't open userquery: mysql init",progname); @NOLOG2@
701    return;
702  }
703  if( ! mysql_real_connect(conn, "localhost", my_user, my_pass, my_db,
704      0,NULL,0) ) {
705    @NOLOG1@ sgLogError("%s: can't open userquery: mysql connect",progname); @NOLOG2@
706    return;
707  }
708  my_query=(char *)calloc(strlen(query) + strlen(str) + 1,sizeof(char));
709  strcat(my_query, query);
710  strcat(my_query, str);
711  /* DEBUG:   sgLogError("%s: TEST: MySQL Query %s",progname,my_query);  */
712  if( mysql_query(conn, my_query) ) {
713    @NOLOG1@ sgLogError("%s: can't open userquery: mysql query",progname); @NOLOG2@
714    return;
715  }
716  res = mysql_use_result(conn);
717  while( row = mysql_fetch_row(res) ) {
718    strncpy(line, row[0], sizeof(line)-1);
719    l++;
720    sgDbUpdate(sp->userDb, line, (char *) setuserinfo(), sizeof(struct UserInfo));
721    sgLogError("Added MySQL source: %s", line);
722  }
723  mysql_free_result(res);
724  mysql_close(conn);
725 }
726#endif
727
728
729/* LDAP Support */
730#ifdef HAVE_LIBLDAP
731#if __STDC__
732void sgSourceLdapUserSearch(char *url)
733#else
734void sgSourceLdapUserSearch(url)
735     char *url;
736#endif
737{
738  struct Source *sp;
739  sp = lastSource;
740
741/*  DEBUG
742  sgLogError("sgSourceLdapUserSearch called with: %s", url);
743*/
744
745  if(!ldap_is_ldap_url(url)) {
746    @NOLOG1@ sgLogError("%s: can't parse LDAP url %s",progname, url);  @NOLOG2@
747    return;
748  }
749
750  /* looks ok, add the url to the source object url array */
751  sp->ldapurls = (char**) sgRealloc(sp->ldapurls,
752                                    sizeof(char*) * (sp->ldapurlcount+1));
753  sp->ldapurls[sp->ldapurlcount] = (char*) sgMalloc(strlen(url) + 1);
754  strcpy(sp->ldapurls[sp->ldapurlcount], url);
755  sp->ldapurlcount++;
756
757  /* create a userDb if it doesn't exist, since we'll need it later
758   * for caching */
759  if(sp->userDb == NULL){
760    sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
761    sp->userDb->type=SGDBTYPE_USERLIST;
762    sgDbInit(sp->userDb,NULL);
763  }
764}
765#endif
766
767#if __STDC__
768void sgSourceExecUserList(char *cmd)
769#else
770void sgSourceExecUserList(cmd)
771     char *cmd;
772#endif
773{
774  FILE *pInput;
775  char buffer[100];
776  struct Source *sp;
777  char *lc;
778  sp = lastSource;
779  if(sp->userDb == NULL){
780    sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
781    sp->userDb->type=SGDBTYPE_USERLIST;
782    sgDbInit(sp->userDb,NULL);
783  }
784
785/*  DEBUG
786  sgLogError("sgSourceExecUserList called with: %s", cmd);
787*/
788
789  pInput = popen(cmd, "r");
790  if(pInput == NULL) {
791    sgLogError("%s: Unable to run execuserlist command: %s", progname, cmd);
792    return;
793  }
794
795  while(fgets(buffer, sizeof(buffer), pInput) != NULL) {
796    char *sc;
797    /* skip leading whitespace */
798    for(sc=buffer; *sc != '\0' && isspace(*sc); sc++)
799    ;
800    /* convert username to lowercase */
801    for(lc=sc; *lc != '\0'; lc++)
802      *lc = tolower(*lc);
803    /* remove newline and trailing whitespace */
804    while(lc>=sc && (*lc=='\0' || isspace(*lc)))
805      *lc-- = '\0';
806    if(lc >= sc) {
807      sgDbUpdate(sp->userDb, sc, (char *) setuserinfo(),
808                 sizeof(struct UserInfo));
809// DEBUG
810      sgLogError("Added exec source: %s", sc);
811    }
812  }
813
814  pclose(pInput);
815}
816
817
818
819#if __STDC__
820void sgSourceUserQuota(char *seconds, char *sporadic, char *renew)
821#else
822void sgSourceUserQuota(seconds, sporadic, renew)
823     char *seconds;
824     char *sporadic;
825     char *renew;
826#endif
827{
828  int s;
829  struct UserQuota *uq;
830  struct Source *sp;
831  sp = lastSource;
832  uq = &sp->userquota;
833  s = atoi(seconds);
834  if(s <= 0)
835    sgLogError("Userquota seconds sporadic hourly|daily|weekly");
836  uq->seconds = s;
837  s = atoi(sporadic);
838  if(s <= 0)
839    sgLogError("Userquota seconds sporadic hourly|daily|weekly");
840  uq->sporadic = s;
841  s = atoi(renew);
842  if(s <= 0)
843    sgLogError("Userquota seconds sporadic hourly|daily|weekly");
844  uq->renew = s;
845}
846
847
848#if __STDC__
849void sgSourceDomain(char *domain)
850#else
851void sgSourceDomain(domain)
852     char *domain;
853#endif
854{
855  struct Source *sp;
856  sp = lastSource;
857  if(sp->domainDb == NULL){
858    sp->domainDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
859    sp->domainDb->type=SGDBTYPE_DOMAINLIST;
860    sgDbInit(sp->domainDb,NULL);
861  }
862  sgDbUpdate(sp->domainDb,domain, NULL, 0);
863}
864
865#if __STDC__
866void sgSourceTime(char *name, int within)
867#else
868void sgSourceTime(name, within)
869     char *name;
870     int within;
871#endif
872{
873  struct Time *time = NULL;
874  struct Source *sp;
875  sp = lastSource;
876  if((time = sgTimeFindName(name)) == NULL){
877    sgLogFatalError("%s: Time %s is not defined in configfile %s",
878		    progname,name, configFile);
879  }
880  sp->within = within;
881  sp->time = time;
882}
883
884#if __STDC__
885struct Source *sgSourceFindName(char *name)
886#else
887struct Source *sgSourceFindName(name)
888     char *name;
889#endif
890{
891  struct Source *p;
892  for(p=Source; p != NULL; p = p->next){
893    if(!strcmp(name,p->name))
894      return p;
895  }
896  return NULL;
897}
898
899#if __STDC__
900void sgSourceIpList(char *file)
901#else
902void sgSourceIpList(file)
903     char *file;
904#endif
905{
906  char *dbhome = NULL, *f;
907  FILE *fd;
908  char line[MAX_BUF];
909  char *p,*c,*cidr;
910  int i,l=0;
911  dbhome = sgSettingGetValue("dbhome");
912  if(dbhome == NULL)
913    dbhome = DEFAULT_DBHOME;
914  if (file[0] == '/') {
915    f = strdup(file);
916  } else {
917    f = (char  *) sgCalloc(1,strlen(dbhome) + strlen(file) + 5);
918    strcpy(f,dbhome);
919    strcat(f,"/");
920    strcat(f,file);
921  }
922  if((fd = fopen(f,"r")) == NULL){
923    sgLogError("%s: can't open iplist %s: %s",progname, f,strerror(errno));
924    return;
925  }
926  sgLogError("init iplist %s",f);
927  while(fgets(line,sizeof(line),fd) != NULL){
928    l++;
929    if(*line == '#')
930      continue;
931    p = strchr(line,'\n');
932    if(p != NULL && p != line){
933      if(*(p - 1) == '\r') /* removing ^M  */
934	p--;
935      *p = '\0';
936    }
937    c = strchr(line,'#');
938    p = strtok(line," \t,");
939    do {
940      if(c != NULL && p >= c) /*find the comment */
941	break;
942      i=strspn(p,".0123456789/-");
943      if(i == 0)
944	break;
945      *(p + i ) = '\0';
946      if((cidr = strchr(p,'/')) != NULL){
947	*cidr = '\0';
948	cidr++;
949	sgIp(p);
950	sgIp(cidr);
951	if(strchr(cidr,'.') == NULL)
952	  sgSetIpType(SG_IPTYPE_CIDR,f,l);
953	else
954	  sgSetIpType(SG_IPTYPE_CLASS,f,l);
955      } else if((cidr = strchr(p,'-')) != NULL) {
956	*cidr = '\0';
957	cidr++;
958	sgIp(p);
959	sgIp(cidr);
960	sgSetIpType(SG_IPTYPE_RANGE,f,l);
961      } else {
962	sgIp(p);
963	sgIp(strdup("255.255.255.255"));
964	sgSetIpType(SG_IPTYPE_HOST,f,l);
965      }
966    } while((p=strtok(NULL," \t,")) != NULL);
967  }
968  fclose(fd);
969}
970
971/*
972
973
974 */
975
976#if __STDC__
977struct Source *sgFindSource (struct Source *bsrc,
978			     char *net, char *ident, char *domain)
979#else
980struct Source *sgFindSource (bsrc, net, ident, domain)
981     struct Source *bsrc;
982     char *net;
983     char *ident;
984     char *domain;
985#endif
986{
987  struct Source *s;
988  struct Ip *ip;
989  int foundip, founduser, founddomain, unblockeduser;
990  unsigned long i, octet = 0, *op;
991  struct UserInfo *userquota;
992  if(net != NULL){
993    op = sgConvDot(net);
994    if(op != NULL)
995      octet = *op;
996  }
997  for(s=bsrc; s != NULL; s = s->next){
998    foundip = founduser = founddomain = 0;
999    unblockeduser = 1;
1000    if(s->active == 0)
1001      continue;
1002    if(s->ip != NULL){
1003      if(net == NULL)
1004	foundip = 0;
1005      else {
1006	for(ip=s->ip; ip != NULL; ip = ip->next){
1007	  if(ip->net_is_set == 0)
1008	    continue;
1009	  if(ip->type == SG_IPTYPE_RANGE){
1010	    if(octet >= ip->net && octet <= ip->mask){
1011	      foundip = 1;
1012	      break;
1013	    }
1014	  } else { /* CIDR or HOST */
1015	    i = octet & ip->mask;
1016	    if(i == ip->net){
1017	      foundip = 1;
1018	      break;
1019	    }
1020	  }
1021	}
1022      }
1023    } else
1024      foundip = 1;
1025    if(s->userDb != NULL){
1026      if(*ident == '\0')
1027	founduser = 0;
1028      else {
1029#ifdef HAVE_LIBLDAP
1030        if(sgFindUser(s, ident, &userquota)) {
1031#else
1032        rfc1738_unescape(ident);
1033        if(defined(s->userDb, ident, (char **) &userquota) == 1){
1034#endif
1035	  founduser = 1;
1036	  unblockeduser = 1;
1037	  if(s->userquota.seconds != 0){
1038            struct UserInfo uq;
1039	    time_t t = time(NULL) + globalDebugTimeDelta;
1040	    //sgLogError("status %d time %d lasttime %d consumed %d", userquota->status, userquota->time, userquota->last, userquota->consumed);
1041	    //sgLogError("renew %d seconds %d", s->userquota.renew, s->userquota.seconds);
1042	    if(userquota->status == 0){ //first time
1043	      userquota->status = 1;
1044	      userquota->time = t;
1045	      userquota->last = t;
1046	      //sgLogError("user %s first time %d", ident, userquota->time);
1047	    } else if(userquota->status == 1){
1048	      //sgLogError("user %s other time %d %d",ident,userquota->time,t);
1049	      if(s->userquota.sporadic > 0){
1050		if(t - userquota->last  < s->userquota.sporadic){
1051		  userquota->consumed += t - userquota->last;
1052		  userquota->time = t;
1053		}
1054		if(userquota->consumed > s->userquota.seconds){
1055		  userquota->status = 2; // block this user, time is up
1056		  unblockeduser = 0;
1057		}
1058		userquota->last = t;
1059		//sgLogError("user %s consumed %d %d",ident,userquota->consumed, userquota->last);
1060	      } else if(userquota->time + s->userquota.seconds < t){
1061		sgLogError("time is up user %s blocket", ident);
1062		userquota->status = 2; // block this user, time is up
1063		unblockeduser = 0;
1064	      }
1065	    } else {
1066	      //sgLogError("user %s blocket %d %d %d %d", ident, userquota->status, userquota->time, t, (userquota->time + s->userquota.renew) - t);
1067	      if(userquota->time + s->userquota.renew < t){ // new chance
1068		//sgLogError("user %s new chance", ident);
1069		unblockeduser = 1;
1070		userquota->status = 1;
1071		userquota->time = t;
1072		userquota->consumed = 0;
1073	      } else
1074		unblockeduser = 0;
1075	    }
1076	    sgDbUpdate(s->userDb, ident, (void *) userquota,
1077                      sizeof(struct UserInfo));
1078	  }
1079	}
1080      }
1081    } else
1082      founduser = 1;
1083    if(s->domainDb != NULL){
1084      if(*domain == '\0')
1085	founddomain = 0;
1086      else {
1087	if(defined(s->domainDb, domain, (char **) NULL) == 1)
1088	  founddomain = 1;
1089      }
1090    } else
1091      founddomain = 1;
1092    if(founduser && foundip && founddomain){
1093      if(unblockeduser)
1094	return s;
1095      else {
1096	lastActiveSource = s;
1097	return NULL;
1098      }
1099    }
1100  }
1101  return NULL;
1102}
1103
1104
1105
1106/*destination block funtions */
1107
1108#if __STDC__
1109void sgDest(char *dest)
1110#else
1111void sgDest(dest)
1112     char *dest;
1113#endif
1114{
1115  struct Destination *sp;
1116  if(Dest != NULL){
1117    if((struct Destination *) sgDestFindName(dest) != NULL)
1118      sgLogFatalError("%s: destination %s is defined in configfile %s",
1119		   progname,dest, configFile);
1120  }
1121  sp = (struct Destination *) sgCalloc(1,sizeof(struct Destination));
1122  sp->domainlist=NULL;
1123  sp->urllist=NULL;
1124  sp->expressionlist=NULL;
1125  sp->redirect=NULL;
1126  sp->rewrite=NULL;
1127  sp->active = 1;
1128  sp->time = NULL;
1129  sp->within = 0;
1130  sp->logfile = NULL;
1131  sp->next=NULL;
1132  sp->name = (char  *) sgCalloc(1,strlen(dest) + 1);
1133  strcpy(sp->name,dest);
1134
1135  if(Dest == NULL){
1136    Dest = sp;
1137    lastDest = sp;
1138  } else {
1139    lastDest->next = sp;
1140    lastDest = sp;
1141  }
1142}
1143
1144void sgDestEnd()
1145{
1146 struct Destination *d;
1147 d = lastDest;
1148 if(d->domainlist == NULL && d->urllist == NULL && d->expressionlist == NULL
1149    && d->redirect == NULL && d->rewrite == NULL){
1150   sgLogError("destblock %s missing active content, set inactive",d->name);
1151   d->time = NULL;
1152   d->active = 0;
1153 }
1154}
1155
1156#if __STDC__
1157void sgDestDomainList(char *domainlist)
1158#else
1159void sgDestDomainList(domainlist)
1160     char *domainlist;
1161#endif
1162{
1163  struct Destination *sp;
1164  char *dbhome = NULL, *dl = domainlist, *name;
1165  dbhome = sgSettingGetValue("dbhome");
1166  sp = lastDest;
1167  if(dbhome == NULL)
1168    dbhome = DEFAULT_DBHOME;
1169 if(domainlist == NULL){
1170    name = sp->name;
1171    dl = (char *) sgCalloc(1,strlen("/dest/") + strlen(name) + strlen("/domainlist"));
1172    strcpy(dl,"/dest/");
1173    strcat(dl,name);
1174    strcat(dl,"/domainlist");
1175    sp->domainlist = (char  *) sgCalloc(1,strlen(dbhome) + strlen("/") + strlen(dl) + 4);
1176    strcpy(sp->domainlist,dbhome);
1177    strcat(sp->domainlist,"/");
1178    strcat(sp->domainlist,dl);
1179    sgFree(dl);
1180  } else {
1181    if (domainlist[0] == '/') {
1182      sp->domainlist = strdup(domainlist);
1183    } else {
1184    sp->domainlist = (char  *) sgCalloc(1,strlen(dbhome) + strlen("/") + strlen(domainlist) + 4);
1185    strcpy(sp->domainlist,dbhome);
1186    strcat(sp->domainlist,"/");
1187    strcat(sp->domainlist,domainlist);
1188    }
1189  }
1190  sp->domainlistDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
1191  sp->domainlistDb->type=SGDBTYPE_DOMAINLIST;
1192  sgLogError("init domainlist %s",sp->domainlist);
1193  sgDbInit(sp->domainlistDb,sp->domainlist);
1194  if(sp->domainlistDb->entries == 0) { /* empty database */
1195    sgLogError("domainlist empty, removed from memory");
1196    sgFree(sp->domainlistDb);
1197    sp->domainlistDb = NULL;
1198  }
1199}
1200
1201#if __STDC__
1202void sgDestUrlList(char *urllist)
1203#else
1204void sgDestUrlList(urllist)
1205     char *urllist;
1206#endif
1207{
1208  struct Destination *sp;
1209  char *dbhome = NULL, *dl = urllist, *name;
1210  dbhome = sgSettingGetValue("dbhome");
1211  sp = lastDest;
1212  if(dbhome == NULL)
1213    dbhome = DEFAULT_DBHOME;
1214  if(urllist == NULL){
1215    name = sp->name;
1216    dl = (char *) sgCalloc(1,strlen("/dest/") + strlen(name) + strlen("/urllist"));
1217    strcpy(dl,"/dest/");
1218    strcat(dl,name);
1219    strcat(dl,"/urllist");
1220    sp->urllist = (char  *) sgCalloc(1,strlen(dbhome) + strlen("/") + strlen(dl) + 4);
1221    strcpy(sp->urllist,dbhome);
1222    strcat(sp->urllist,"/");
1223    strcat(sp->urllist,dl);
1224    sgFree(dl);
1225  } else {
1226    if (urllist[0] == '/') {
1227      sp->urllist = strdup(urllist);
1228    } else {
1229    sp->urllist = (char  *) sgCalloc(1,strlen(dbhome) + strlen("/") + strlen(urllist) + 4);
1230    strcpy(sp->urllist,dbhome);
1231    strcat(sp->urllist,"/");
1232    strcat(sp->urllist,urllist);
1233    }
1234  }
1235  sp->urllistDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb));
1236  sp->urllistDb->type=SGDBTYPE_URLLIST;
1237  sgLogError("init urllist %s",sp->urllist);
1238  sgDbInit(sp->urllistDb,sp->urllist);
1239  if(sp->urllistDb->entries == 0) { /* empty database */
1240    sgLogError("urllist empty, removed from memory");
1241    sgFree(sp->urllistDb);
1242    sp->urllistDb = NULL;
1243  }
1244}
1245
1246#if __STDC__
1247void sgDestExpressionList(char *exprlist, char *chcase)
1248#else
1249void sgDestExpressionList(exprlist, chcase)
1250     char *exprlist;
1251     char *chcase;
1252#endif
1253{
1254  FILE *fp;
1255  char buf[MAX_BUF],errbuf[256];
1256  struct Destination *sp;
1257  struct sgRegExp *regexp;
1258  char *dbhome = NULL, *dl = exprlist, *name, *p;
1259  int flags = REG_EXTENDED;
1260  dbhome = sgSettingGetValue("dbhome");
1261  sp = lastDest;
1262  if(dbhome == NULL)
1263    dbhome = DEFAULT_DBHOME;
1264  if(exprlist == NULL){
1265    name = sp->name;
1266    dl = (char *) sgCalloc(1,strlen("/dest/") +strlen(name) + strlen("/expressionlist"));
1267    strcpy(dl,"/dest/");
1268    strcat(dl,name);
1269    strcat(dl,"/expressionlist");
1270    flags |= REG_ICASE; /* default case insensitive */
1271    sp->expressionlist = (char  *) sgCalloc(1,strlen(dbhome)+strlen(dl)+10);
1272    strcpy(sp->expressionlist,dbhome);
1273    strcat(sp->expressionlist,"/");
1274    strcat(sp->expressionlist,dl);
1275    sgFree(dl);
1276  } else {
1277    if (exprlist[0] == '/') {
1278      sp->expressionlist = strdup(exprlist);
1279    } else {
1280    sp->expressionlist = (char  *) sgCalloc(1,strlen(dbhome) + strlen("/") + strlen(exprlist) + 4);
1281    strcpy(sp->expressionlist,dbhome);
1282    strcat(sp->expressionlist,"/");
1283    strcat(sp->expressionlist,exprlist);
1284    }
1285    if(strncmp(chcase,"c",1))
1286          flags |= REG_ICASE; /* set case insensitive */
1287  }
1288  sgLogError("init expressionlist %s",sp->expressionlist);
1289  if ((fp = fopen(sp->expressionlist, "r")) == NULL)
1290    sgLogFatalError("%s: %s", sp->expressionlist, strerror(errno));
1291  while(fgets(buf, sizeof(buf), fp) != NULL){
1292    p = (char *) strchr(buf,'\n');
1293    if(p != NULL && p != buf){
1294      if(*(p - 1) == '\r') /* removing ^M  */
1295	p--;
1296      *p = '\0';
1297    }
1298    regexp=sgNewPatternBuffer(buf,flags);
1299    if(regexp->error){
1300      regerror(regexp->error,regexp->compiled, errbuf,sizeof(errbuf));
1301      sgLogError("%s: %s", sp->expressionlist, strerror(errno));
1302    }
1303    if(lastDest->regExp == NULL){
1304      lastDest->regExp = regexp;
1305      lastRegExpDest = regexp;
1306    } else {
1307      lastRegExpDest->next = regexp;
1308      lastRegExpDest = regexp;
1309    }
1310  }
1311  fclose(fp);
1312}
1313
1314#if __STDC__
1315void sgDestRedirect(char *value)
1316#else
1317void sgDestRedirect(value)
1318     char *value;
1319#endif
1320{
1321  struct Destination *sp;
1322  sp = lastDest;
1323  sp->redirect = (char *) sgCalloc(1,strlen(value) + 1);
1324  strcpy(sp->redirect,value);
1325}
1326
1327void sgDestRewrite(char *value){
1328  struct sgRewrite *rewrite = NULL;
1329  struct Destination *sp;
1330  sp = lastDest;
1331  if((rewrite = sgRewriteFindName(value)) == NULL){
1332    sgLogFatalError("%s: Rewrite %s is not defined in configfile %s",
1333		    progname,value, configFile);
1334  }
1335  sp->rewrite = rewrite;
1336}
1337
1338#if __STDC__
1339int sgRegExpMatch(struct sgRegExp *regexp, char *str)
1340#else
1341int sgRegExpMatch(regexp, str)
1342     struct sgRegExp *regexp;
1343     char *str;
1344#endif
1345{
1346  struct sgRegExp *rp;
1347  static char errbuf[256];
1348  int error;
1349  for(rp = regexp; rp != NULL; rp = rp->next){
1350    error = regexec(rp->compiled, str, 0,0,0);
1351    if(error != 0 && error != REG_NOMATCH) {
1352      regerror(error,rp->compiled, errbuf,sizeof(errbuf));
1353      sgLogError("Error in regex %30.30s %-60.60s  %d %s\n",rp->pattern,str,error,errbuf);
1354    }
1355    if(error == 0) /* match */
1356      return 1;
1357  }
1358  return 0;
1359}
1360
1361#if __STDC__
1362void sgDestTime(char *name, int within)
1363#else
1364void sgDestTime(name, within)
1365     char *name;
1366     int within;
1367#endif
1368{
1369  struct Time *time = NULL;
1370  struct Destination *sp;
1371  sp = lastDest;
1372  if((time = sgTimeFindName(name)) == NULL){
1373    sgLogFatalError("%s: Time %s is not defined in configfile %s",
1374		    progname,name, configFile);
1375  }
1376  sp->within = within;
1377  sp->time = time;
1378}
1379
1380#if __STDC__
1381struct Destination *sgDestFindName(char *name)
1382#else
1383struct Destination *sgDestFindName(name)
1384     char *name;
1385#endif
1386{
1387  struct Destination *p;
1388  for(p=Dest; p != NULL; p = p->next){
1389    if(!strcmp(name,p->name))
1390      return p;
1391  }
1392  return NULL;
1393}
1394
1395/*
1396  Setting functions
1397*/
1398
1399
1400#if __STDC__
1401void sgSetting(char *name, char *value)
1402#else
1403void sgSetting(name, value)
1404     char *name;
1405     char *value;
1406#endif
1407{
1408  struct Setting *sp;
1409  if(Setting != NULL){
1410    if((struct Setting *) sgSettingFindName(name) != NULL)
1411      sgLogFatalError("%s: setting %s is defined in configfile %s",
1412		      progname,name, configFile);
1413  }
1414  sp = (struct Setting *) sgCalloc(1,sizeof(struct Setting));
1415
1416  sp->name = strdup(name);
1417  sp->value = strdup(value);
1418
1419// DEBUG
1420  sgLogError("New setting: %s: %s", name, value);
1421
1422
1423  if(Setting == NULL){
1424    Setting = sp;
1425    lastSetting = sp;
1426  } else {
1427    lastSetting->next = sp;
1428    lastSetting = sp;
1429  }
1430  if(!strcmp(name,"logdir")){
1431    globalLogDir= strdup(value);
1432  }
1433}
1434
1435#if __STDC__
1436struct Setting *sgSettingFindName(char *name)
1437#else
1438struct Setting *sgSettingFindName(name)
1439     char *name;
1440#endif
1441{
1442  struct Setting *p;
1443  for(p=Setting; p != NULL; p = p->next){
1444    if(!strcmp(name,p->name))
1445      return p;
1446  }
1447  return NULL;
1448}
1449
1450
1451#if __STDC__
1452char *sgSettingGetValue(char *name)
1453#else
1454char *sgSettingGetValue(name)
1455     char *name;
1456#endif
1457{
1458  struct Setting *p;
1459  p = sgSettingFindName(name);
1460  if(p != NULL)
1461    return p->value;
1462  return NULL;
1463}
1464
1465
1466/*
1467
1468  sgRewrite function
1469
1470 */
1471
1472#if __STDC__
1473void sgRewrite(char *rewrite)
1474#else
1475void sgRewrite(rewrite)
1476     char *rewrite;
1477#endif
1478{
1479  struct sgRewrite *rew;
1480  if(Rewrite != NULL){
1481    if((struct sgRewrite *) sgRewriteFindName(rewrite) != NULL)
1482      sgLogFatalError("%s: rewrite %s is defined in configfile %s",
1483		      progname,rewrite, configFile);
1484  }
1485  rew = (struct sgRewrite *) sgCalloc(1,sizeof(struct sgRewrite));
1486  rew->name = strdup(rewrite);
1487  rew ->rewrite = NULL;
1488  rew->logfile = NULL;
1489  rew->time = NULL;
1490  rew->active = 1;
1491  rew->within = 0;
1492  rew->next=NULL;
1493
1494  if(Rewrite == NULL){
1495    Rewrite = rew;
1496    lastRewrite = rew;
1497  } else {
1498    lastRewrite->next = rew;
1499    lastRewrite = rew;
1500  }
1501}
1502
1503#if __STDC__
1504void sgRewriteTime(char *name, int within)
1505#else
1506void sgRewriteTime(name, within)
1507     char *name;
1508     int within;
1509#endif
1510{
1511  struct Time *time = NULL;
1512  struct sgRewrite *sp;
1513  sp = lastRewrite;
1514  if((time = sgTimeFindName(name)) == NULL){
1515    sgLogFatalError("%s: Time %s is not defined in configfile %s",
1516		    progname,name, configFile);
1517  }
1518  sp->within = within;
1519  sp->time = time;
1520}
1521
1522#if __STDC__
1523void sgRewriteSubstitute (char *string)
1524#else
1525void sgRewriteSubstitute (string)
1526     char *string;
1527#endif
1528{
1529  char *pattern, *subst = NULL , *p;
1530  int flags = REG_EXTENDED ;
1531  int global = 0;
1532  char *httpcode = NULL;
1533  struct sgRegExp *regexp;
1534  char errbuf[256];
1535  pattern = string + 2 ; /* skipping s@ */
1536  p = pattern;
1537  while((p = strchr(p,'@')) != NULL){
1538    if(*( p - 1) != '\\'){
1539      *p = '\0';
1540      subst = p + 1;
1541      break;
1542    }
1543    p++;
1544  }
1545  p= strrchr(subst,'@');
1546  while(p != NULL && *p != '\0'){
1547    if(*p == 'r' )
1548      httpcode =  REDIRECT_TEMPORARILY;
1549    if(*p == 'R' )
1550      httpcode =  REDIRECT_PERMANENT;
1551    if(*p == 'i' || *p == 'I')
1552      flags |= REG_ICASE;
1553    if(*p == 'g')
1554      global = 1;
1555    *p = '\0'; /*removes @i from string */
1556    p++;
1557  }
1558  regexp=sgNewPatternBuffer(pattern,flags);
1559  if(regexp->error){
1560      regerror(regexp->error,regexp->compiled, errbuf,sizeof(errbuf));
1561      sgLogError("Error in regexp %s: %s",pattern,errbuf);
1562  } else {
1563    regexp->substitute = strdup(subst);
1564  }
1565  if(lastRewrite->rewrite == NULL)
1566    lastRewrite->rewrite = regexp;
1567  else
1568    lastRewriteRegExec->next=regexp;
1569  regexp->httpcode = httpcode;
1570  regexp->global = global;
1571  lastRewriteRegExec = regexp;
1572}
1573
1574#if __STDC__
1575char *sgRewriteExpression(struct sgRewrite *rewrite, char *subst)
1576#else
1577char *sgRewriteExpression(rewrite, subst)
1578     struct sgRewrite *rewrite;
1579     char *subst;
1580#endif
1581{
1582  char *result = NULL;
1583  result = sgRegExpSubst(rewrite->rewrite, subst);
1584  return result;
1585}
1586
1587#if __STDC__
1588struct sgRewrite *sgRewriteFindName(char *name)
1589#else
1590struct sgRewrite *sgRewriteFindName(name)
1591     char *name;
1592#endif
1593{
1594  struct sgRewrite *p;
1595  for(p=Rewrite; p != NULL; p = p->next){
1596    if(!strcmp(name,p->name))
1597      return p;
1598  }
1599  return NULL;
1600}
1601
1602
1603
1604/*
1605  Time functions
1606*/
1607
1608#if __STDC__
1609void sgTime(char *name)
1610#else
1611void sgTime(name)
1612     char *name;
1613#endif
1614{
1615  struct Time *t;
1616  if(Time != NULL){
1617    if((struct Time *) sgTimeFindName(name) != NULL)
1618      sgLogFatalError("%s: time %s is defined in configfile %s",
1619		      progname,name, configFile);
1620  } else
1621    numTimeElements = 0;
1622  t = (struct Time *) sgCalloc(1,sizeof(struct Time));
1623  t->name = strdup(name);
1624  t->element = NULL;
1625  t->active = 1;
1626  TimeElement = NULL;
1627  lastTimeElement = NULL;
1628  if(Time == NULL){
1629    Time = t;
1630    lastTime = t;
1631  } else {
1632    lastTime->next = t;
1633    lastTime = t;
1634  }
1635}
1636
1637#if __STDC__
1638void sgTimeElementInit()
1639#else
1640void sgTimeElementInit()
1641#endif
1642{
1643  struct TimeElement *te;
1644  te = (struct TimeElement *) sgCalloc(1,sizeof(struct TimeElement));
1645  numTimeElements++;
1646  if(lastTime->element == NULL)
1647    lastTime->element = te;
1648  if(lastTimeElement != NULL)
1649    lastTimeElement->next = te;
1650  lastTimeElement = te;
1651}
1652
1653#if __STDC__
1654void sgTimeElementEnd ()
1655#else
1656void sgTimeElementEnd ()
1657#endif
1658{
1659  time_switch = 0;
1660  date_switch = 0;
1661  if(lastTimeElement->fromdate !=0){
1662    if(lastTimeElement->todate == 0)
1663      lastTimeElement->todate = lastTimeElement->fromdate + 86399;
1664    else
1665      lastTimeElement->todate = lastTimeElement->todate + 86399;
1666  }
1667  if(lastTimeElement->from == 0 && lastTimeElement->to == 0)
1668    lastTimeElement->to = 1439; /* set time to 23:59 */
1669}
1670
1671#if __STDC__
1672void sgTimeElementAdd (char *element, char type)
1673#else
1674void sgTimeElementAdd (element, type)
1675     char *element;
1676     char type;
1677#endif
1678{
1679  struct TimeElement *te;
1680  char *p;
1681  char wday = 0;
1682  int h,m,Y,M = 0,D = -1;
1683  time_t sec;
1684  te = lastTimeElement;
1685  switch(type) {
1686  case T_WEEKDAY:
1687    p = strtok(element," \t,");
1688    do {
1689      if(*p == '*'){
1690	wday = 127;
1691      } else if(!strncmp(p,"sun",3)){
1692	wday = wday | 0x01;
1693      } else if(!strncmp(p,"mon",3)){
1694	wday = wday | 0x02;
1695      } else if(!strncmp(p,"tue",3)){
1696	wday = wday | 0x04;
1697      } else if(!strncmp(p,"wed",3)){
1698	wday = wday | 0x08;
1699      } else if(!strncmp(p,"thu",3)){
1700	wday = wday | 0x10;
1701      } else if(!strncmp(p,"fri",3)){
1702	wday = wday | 0x20;
1703      } else if(!strncmp(p,"sat",3)){
1704	wday = wday | 0x40;
1705      }
1706      p=strtok(NULL," \t,");
1707    } while(p != NULL);
1708    te->wday = wday;
1709    break;
1710  case T_TVAL:
1711    sscanf(element,"%d:%d",&h,&m);
1712    if((h < 0 && h > 24) && (m < 0 && m > 59))
1713      sgLogFatalError("%s: time formaterror in %s line %d",
1714		      progname, configFile,lineno);
1715    if(time_switch == 0){
1716      time_switch++;
1717      te->from = (h * 60) + m ;
1718    } else {
1719      time_switch=0;
1720      te->to = (h * 60) + m ;
1721    }
1722    break;
1723  case T_DVAL:
1724    sec = date2sec(element);
1725    if(sec == -1){
1726      sgLogFatalError("%s: date formaterror in %s line %d",
1727		      progname, configFile,lineno);
1728    }
1729    if(date_switch == 0){
1730      date_switch++;
1731      te->fromdate = sec;
1732    } else {
1733      date_switch=0;
1734      te->todate = sec;
1735    }
1736    break;
1737  case T_DVALCRON:
1738    p = strtok(element,"-.");
1739    Y = atoi(p);
1740    if(*p == '*')
1741      Y = -1;
1742    else
1743      Y = atoi(p);
1744    while((p=strtok(NULL,"-.")) != NULL){
1745      if(*p == '*')
1746	if(M == 0)
1747	  M = -1;
1748	else
1749	  D = -1;
1750      else
1751	if(M == 0)
1752	  M = atoi(p);
1753	else
1754	  D = atoi(p);
1755    }
1756    te->y=Y; te->m=M; te->d=D;
1757    break;
1758  case T_WEEKLY:
1759    p = element;
1760    while(*p != '\0'){
1761      switch(*p){
1762      case 'S':
1763      case 's':
1764	wday = wday | 0x01;
1765	break;
1766      case 'M':
1767      case 'm':
1768	wday = wday | 0x02;
1769	break;
1770      case 'T':
1771      case 't':
1772	wday = wday | 0x04;
1773	break;
1774      case 'W':
1775      case 'w':
1776	wday = wday | 0x08;
1777	break;
1778      case 'H':
1779      case 'h':
1780	wday = wday | 0x10;
1781	break;
1782      case 'F':
1783      case 'f':
1784	wday = wday | 0x20;
1785	break;
1786      case 'A':
1787      case 'a':
1788	wday = wday | 0x40;
1789	break;
1790      default:
1791	sgLogFatalError("%s: weekday formaterror in %s line %d",
1792			progname, configFile,lineno);
1793	break;
1794      }
1795      p++;
1796    }
1797    te->wday = wday;
1798    break;
1799  }
1800}
1801
1802
1803#if __STDC__
1804struct Time *sgTimeFindName(char *name)
1805#else
1806struct Time *sgTimeFindName(name)
1807     char *name;
1808#endif
1809{
1810  struct Time *p;
1811  for(p=Time; p != NULL; p = p->next){
1812    if(!strcmp(name,p->name))
1813      return p;
1814  }
1815  return NULL;
1816}
1817
1818#if __STDC__
1819int sgTimeCmp(const int *a, const int *b)
1820#else
1821int sgTimeCmp(a, b)
1822     const int *a;
1823     const int *b;
1824#endif
1825{
1826  return *a - *b;
1827}
1828
1829#if __STDC__
1830void sgTimeElementSortEvents()
1831#else
1832void sgTimeElementSortEvents()
1833#endif
1834{
1835 struct Time *p;
1836 struct TimeElement *te;
1837 int i = 0,j;
1838 int *t;
1839 if(Time != NULL){
1840   TimeElementsEvents = (int *) sgCalloc(numTimeElements * 2 , sizeof(int));
1841   t = (int *) sgCalloc(numTimeElements * 2, sizeof(int));
1842   for(p = Time; p != NULL; p = p->next){
1843     for(te = p->element; te != NULL; te = te->next){
1844       TimeElementsEvents[i++]= te->from == 0 ? 1440 : te->from;
1845       TimeElementsEvents[i++]= te->to == 0 ? 1440 : te->to;
1846     }
1847   }
1848   qsort(TimeElementsEvents,numTimeElements * 2,sizeof(int),
1849	 (void *) &sgTimeCmp);
1850   for(i=0,j=0; i < numTimeElements * 2; i++){
1851     if(j==0){
1852       t[j++] = TimeElementsEvents[i];
1853     } else {
1854       if(t[j-1] != TimeElementsEvents[i]){
1855	 t[j++]=TimeElementsEvents[i];
1856       }
1857     }
1858   }
1859   sgFree(TimeElementsEvents);
1860   numTimeElements = j;
1861   TimeElementsEvents = t;
1862 }
1863}
1864
1865#if __STDC__
1866int sgTimeNextEvent()
1867#else
1868int sgTimeNextEvent()
1869#endif
1870{
1871  time_t t;
1872  struct tm *lt;
1873  int m = 0;
1874  static int lastval= 0;
1875  static int index = 0;
1876#if HAVE_SIGACTION
1877  struct sigaction act;
1878#endif
1879  if(Time == NULL)
1880    return 0;
1881  t = time(NULL) + globalDebugTimeDelta;
1882
1883  lt = localtime(&t);
1884  m = (lt->tm_hour * 60) + lt->tm_min ;
1885
1886  for(index=0; index < numTimeElements; index++){
1887    if(TimeElementsEvents[index] >= m){
1888      break;
1889    }
1890  }
1891  lastval = TimeElementsEvents[index];
1892#if HAVE_SIGACTION
1893#ifndef SA_NODEFER
1894#define SA_NODEFER 0
1895#endif
1896  act.sa_handler = sgAlarm;
1897  act.sa_flags = SA_NODEFER | SA_RESTART;
1898  sigaction(SIGALRM, &act, NULL);
1899#else
1900#if HAVE_SIGNAL
1901  signal(SIGALRM, &sgAlarm);
1902#else
1903#endif
1904#endif
1905  if(lastval < m )
1906    m = (((1440 - m ) + lastval) * 60) - lt->tm_sec;
1907  else
1908    m = ((lastval - m) * 60) - lt->tm_sec;
1909  if(m <= 0)
1910    m = 30;
1911@NOLOG1@  sgLogError("Info: recalculating alarm in %d seconds", (unsigned int)m); @NOLOG2@
1912  alarm((unsigned int) m);
1913  sgTimeCheck(lt,t);
1914  sgTimeSetAcl();
1915  return 0;
1916}
1917
1918#if __STDC__
1919int sgTimeCheck(struct tm *lt, time_t t)
1920#else
1921int sgTimeCheck(lt, t)
1922     struct tm *lt;
1923     time_t t;
1924#endif
1925{
1926  struct Time *sg;
1927  struct TimeElement *te;
1928  int min;
1929  if(Time == NULL)
1930    return -1;
1931  for(sg = Time; sg != NULL; sg = sg->next){
1932    sg->active = 0;
1933    for(te = sg->element; te != NULL ; te = te->next){
1934      if(te->wday != 0){
1935	if(((1 << lt->tm_wday ) & te->wday) != 0) {
1936	  min = (lt->tm_hour * 60 ) + lt->tm_min;
1937	  if(min >= te->from && min < te->to){
1938	    sg->active = 1;
1939	    break;
1940	  }
1941	}
1942      } else { /* date */
1943	if(te->fromdate != 0){
1944	  if(t >= te->fromdate && t <= te->todate){
1945	    min = (lt->tm_hour * 60 ) + lt->tm_min;
1946	    if(min >= te->from && min < te->to){
1947	      sg->active =1;
1948	      break;
1949	    }
1950	  }
1951	} else { /* cron */
1952	  if(te->y == -1 || te->y == (lt->tm_year + 1900)){
1953	    if(te->m == -1 || te->m == (lt->tm_mon + 1)){
1954	      if(te->d == -1 || te->d == (lt->tm_mday)){
1955		min = (lt->tm_hour * 60 ) + lt->tm_min;
1956		if(min >= te->from && min < te->to){
1957		  sg->active =1;
1958		  break;
1959		}
1960	      }
1961	    }
1962	  }
1963	}
1964      }
1965    }
1966  }
1967  return 0;
1968}
1969
1970void sgTimeSetAcl()
1971{
1972  struct Acl *acl = defaultAcl;
1973  struct Destination *d;
1974  struct Source *s;
1975  struct sgRewrite *rew;
1976  for(acl=Acl; acl != NULL; acl = acl->next){
1977    if(acl->time != NULL){
1978      acl->active = acl->time->active;
1979      if(acl->within == OUTSIDE){
1980	if(acl->active){
1981	  acl->active = 0;
1982        }
1983	else {
1984	  acl->active = 1;
1985        }
1986      }
1987      if(acl->next != NULL && acl->next->within == ELSE){
1988	if(acl->active == 0){
1989	  acl->next->active = 1;
1990	} else {
1991	  acl->next->active = 0;
1992	}
1993      }
1994    }
1995  }
1996  for(d = Dest; d != NULL; d = d->next){
1997    if(d->time != NULL){
1998      d->active = d->time->active;
1999      if(d->within == OUTSIDE){
2000	if(d->active){
2001	  d->active = 0;
2002	} else {
2003	  d->active = 1;
2004        }
2005      }
2006    }
2007  }
2008  for(s = Source; s != NULL; s = s->next){
2009    if(s->time != NULL){
2010      s->active = s->time->active;
2011      if(s->within == OUTSIDE){
2012	if(s->active){
2013	  s->active = 0;
2014        }
2015	else  {
2016	  s->active = 1;
2017        }
2018      }
2019    }
2020  }
2021  for(rew = Rewrite; rew != NULL; rew = rew->next){
2022    if(rew->time != NULL){
2023      rew->active = rew->time->active;
2024      if(rew->within == OUTSIDE)
2025	if(rew->active)
2026	  rew->active = 0;
2027	else
2028	  rew->active = 1;
2029    }
2030  }
2031}
2032
2033void sgTimeElementClone() {
2034  struct TimeElement *te = lastTimeElement, *tmp;
2035  if ( lastTimeElement == NULL ) {
2036    sgLogFatalError("No prev TimeElement in sgTimeElementClone !");
2037  } else {
2038    sgTimeElementInit();
2039    lastTimeElement->wday = te->wday;
2040    lastTimeElement->from = te->from;
2041    lastTimeElement->to = te->to;
2042    lastTimeElement->y = te->y;
2043    lastTimeElement->m = te->m;
2044    lastTimeElement->d = te->d;
2045    lastTimeElement->fromdate = te->fromdate;
2046    lastTimeElement->todate = te->todate;
2047    tmp = lastTimeElement;
2048    lastTimeElement = te;
2049    sgTimeElementEnd();
2050    lastTimeElement = tmp;
2051  }
2052}
2053
2054void sgTimePrint() {
2055  struct Time *t;
2056  struct TimeElement *te;
2057  for(t = Time; t != NULL; t = t->next){
2058    printf("Time %s is ",t->name);
2059    t->active ? printf("active\n") : printf("inactive\n");
2060    for(te = t->element; te != NULL; te = te->next){
2061      printf("\tte->wday     = %x\n",te->wday);
2062      printf("\tte->from     = %d\n",te->from);
2063      printf("\tte->to       = %d\n",te->to);
2064      printf("\tte->y,m,d    = %d-%d-%d\n",te->y,te->m,te->d);
2065      printf("\tte->fromdate = %s\n",te->fromdate == 0 ?
2066             "0" : niso(te->fromdate));
2067      printf("\tte->todate   = %s\n\n",te->todate == 0 ?
2068             "0" : niso(te->todate));
2069    }
2070  }
2071}
2072
2073
2074/*
2075  Ip functions
2076*/
2077
2078
2079#if __STDC__
2080void sgSetIpType(int type, char *file, int line)
2081#else
2082void sgSetIpType(type, file, line)
2083     int type;
2084     char *file;
2085     int line;
2086#endif
2087{
2088  struct Ip *ip = sgIpLast(lastSource),*nip;
2089  char *p;
2090  char *f = file == NULL ? configFile : file;
2091  int l = line == 0 ? lineno : line ;
2092  unsigned long octet, *op = NULL;
2093  if(type == SG_IPTYPE_HOST)
2094    ip->mask = 0xffffffff;
2095  if(type == SG_IPTYPE_RANGE){
2096    if((op=sgConvDot(ip->str)) == NULL)
2097      sgLogFatalError("%s: address error in %s line %d", progname, f,l);
2098    else
2099      ip->mask = *op;
2100    if(ip->net > ip->mask)
2101      sgLogFatalError("%s: iprange error in %s line %d", progname, f,l);
2102  }
2103  if(type == SG_IPTYPE_CLASS){
2104    p=ip->str;
2105    if(*p == '/')
2106      p++;
2107    if((op=sgConvDot(p)) == NULL)
2108      sgLogFatalError("%s: address error in %s line %d", progname, f,l);
2109    else
2110      ip->mask = *op;
2111  }
2112  if(type == SG_IPTYPE_CIDR){
2113    p=ip->str;
2114    if(*p == '/')
2115      p++;
2116    octet = atoi(p);
2117    if(octet < 0 || octet > 32){
2118      sgLogFatalError("%s: prefix error /%s in %s line %d",
2119		      progname,p, f,l);
2120    }
2121    if(octet == 32)
2122      ip->mask = 0xffffffff;
2123    else
2124      ip->mask = 0xffffffff ^ (0xffffffff >> octet);
2125    ip->net = ip->net & ip->mask;
2126  }
2127  ip->type = type;
2128  nip = (struct Ip *) sgCalloc(1,sizeof(struct Ip));
2129  ip->next = nip ;
2130}
2131
2132#if __STDC__
2133void sgIp(char *name)
2134#else
2135void sgIp(name)
2136     char *name;
2137#endif
2138{
2139  struct Ip *ip;
2140  unsigned long *op;
2141  if(lastSource->ip == NULL){
2142    ip = (struct Ip *) sgCalloc(1,sizeof(struct Ip));
2143    ip->next = NULL;
2144    lastSource->ip = ip;
2145    lastSource->lastip = ip;
2146  } else {
2147    ip = sgIpLast(lastSource);
2148  }
2149  if(ip->net_is_set == 0){
2150    ip->net_is_set = 1;
2151    if((op=sgConvDot(name)) == NULL){
2152      sgLogFatalError("%s: address error in %s line %d", progname, configFile,lineno);
2153    } else
2154      ip->net = *op;
2155  } else {
2156    ip->str = (char *) sgCalloc(1,strlen(name) + 1);
2157    strcpy(ip->str,name);
2158  }
2159}
2160
2161#if __STDC__
2162struct Ip *sgIpLast(struct Source *s)
2163#else
2164struct Ip *sgIpLast(s)
2165     struct Source *s;
2166#endif
2167{
2168  struct Ip *ip,*ret = NULL ;
2169  for(ip=s->ip; ip != NULL; ip = ip->next)
2170    ret = ip;
2171  return ret;
2172}
2173
2174/*
2175  ACL functions
2176*/
2177
2178
2179#if __STDC__
2180void sgAcl(char *name, char *value, int within)
2181#else
2182void sgAcl(name, value, within)
2183     char *name;
2184     char *value;
2185     int within;
2186#endif
2187{
2188  struct Acl *acl;
2189  struct Source *source = NULL;
2190  struct Time *time = NULL;
2191  int def = 0;
2192  char *s;
2193  if(name != NULL){
2194    /* due to some strange things in my yacc code */
2195    if((s=(char *) strchr(name,' ')) != NULL)
2196      *s='\0';
2197    if((s=(char *) strchr(name,'\t')) != NULL)
2198      *s='\0';
2199    /*
2200    if(Acl != NULL){
2201      if((struct Acl *) sgAclFindName(name) != NULL){
2202	sgLogFatalError("%s: ACL %s is defined in configfile %s",
2203			progname,name, configFile);
2204      }
2205    }
2206    */
2207  }
2208  if(lastAcl != NULL && name == NULL && within == ELSE)
2209    name = lastAcl->name;
2210  acl = (struct Acl *)sgCalloc(1,sizeof(struct Acl));
2211  if(!strcmp(name,"default")){
2212    defaultAcl=acl;
2213    def++;
2214  } else {
2215    if((source = sgSourceFindName(name)) == NULL && !def){
2216      sgLogFatalError("%s: ACL source %s is not defined in configfile %s",
2217		      progname,name, configFile);
2218    }
2219  }
2220  acl->name = sgCalloc(1,strlen(name) + 1);
2221  strcpy(acl->name,name);
2222  acl->active = within == ELSE ? 0 : 1;
2223  acl->source = source;
2224  acl->pass = NULL;
2225  acl->rewriteDefault = 1;
2226  acl->rewrite = NULL;
2227  acl->redirect = NULL;
2228  acl->within = within;
2229  acl->logfile = NULL;
2230  acl->next = NULL;
2231  if(value != NULL){
2232    if((time = sgTimeFindName(value)) == NULL){
2233      sgLogFatalError("%s: ACL time %s is not defined in configfile %s",
2234		      progname,value, configFile);
2235    }
2236    acl->time = time;
2237  }
2238  if(Acl == NULL){
2239    Acl = acl;
2240    lastAcl = acl;
2241  } else {
2242    lastAcl->next = acl;
2243    lastAcl = acl;
2244  }
2245}
2246
2247#if __STDC__
2248void sgAclSetValue (char *what, char *value, int allowed)
2249#else
2250void sgAclSetValue (what, value, allowed)
2251     char *what;
2252     char *value;
2253     int allowed;
2254#endif
2255{
2256  struct Destination *dest = NULL;
2257  struct sgRewrite *rewrite = NULL;
2258  struct AclDest *acldest;
2259  int type = ACL_TYPE_TERMINATOR;
2260  if(!strcmp(what,"pass")){
2261    if(!strcmp(value,"any") || !strcmp(value,"all"))
2262      allowed = 1;
2263    else if(!strcmp(value,"none"))
2264      allowed=0;
2265    else if(!strcmp(value,"in-addr")){
2266      type = ACL_TYPE_INADDR;
2267    } else {
2268      if((dest = sgDestFindName(value)) == NULL){
2269	sgLogFatalError("%s: ACL destination %s is not defined in configfile %s",
2270			progname,value, configFile);
2271      }
2272      type = ACL_TYPE_DEFAULT;
2273    }
2274
2275    acldest = sgCalloc(1,sizeof(struct AclDest));
2276    acldest->name = (char *) sgCalloc(1,strlen(value) + 1);
2277    strcpy(acldest->name,value);
2278    acldest->dest = dest;
2279    acldest->access = allowed;
2280    acldest->type = type;
2281    acldest->next = NULL;
2282    if(lastAcl->pass == NULL){
2283      lastAcl->pass = acldest;
2284    } else {
2285      lastAclDest->next = acldest;
2286    }
2287    lastAclDest = acldest;
2288  }
2289
2290  if(!strcmp(what,"rewrite")){
2291    if(!strcmp(value,"none")){
2292      lastAcl->rewriteDefault = 0;
2293      lastAcl->rewrite = NULL;
2294    } else {
2295      if((rewrite = sgRewriteFindName(value)) == NULL){
2296	sgLogFatalError("%s: Rewrite %s is not defined in configfile %s",
2297			progname,value, configFile);
2298      }
2299      lastAcl->rewriteDefault = 0;
2300      lastAcl->rewrite = rewrite;
2301    }
2302  }
2303  if(!strcmp(what,"redirect")){
2304    if(strcmp(value,"default")){
2305      lastAcl->redirect = (char *) sgCalloc(1,strlen(value) + 1);
2306      strcpy(lastAcl->redirect,value);
2307    } else {
2308      lastAcl->redirect= NULL;
2309    }
2310  }
2311}
2312
2313#if __STDC__
2314struct Acl *sgAclFindName(char *name)
2315#else
2316struct Acl *sgAclFindName(name)
2317     char *name;
2318#endif
2319{
2320  struct Acl *p;
2321  for(p=Acl; p != NULL; p = p->next){
2322    if(!strcmp(name,p->name))
2323      return p;
2324  }
2325  return NULL;
2326}
2327
2328
2329#if __STDC__
2330struct Acl *sgAclCheckSource(struct Source *source)
2331#else
2332struct Acl *sgAclCheckSource(source)
2333     struct Source *source;
2334#endif
2335{
2336  struct Acl *acl = defaultAcl;
2337  int found = 0;
2338  if(source != NULL){
2339    for(acl=Acl; acl != NULL; acl = acl->next){
2340      if(acl->source == source){
2341	if(acl->active){
2342	  found++;
2343	  break;
2344	} else {
2345	  if(acl->next->source == source && acl->next->active != 0){
2346	    found++;
2347	    acl=acl->next;
2348	    break;
2349	  }
2350	}
2351      }
2352    }
2353  }
2354@NOLOG1@
2355  else {
2356      if( globalDebug == 1 ) { sgLogError("source not found"); }
2357       }
2358@NOLOG2@
2359  if(!found) {
2360    acl = defaultAcl;
2361@NOLOG1@
2362    if( globalDebug == 1 ) { sgLogError("no ACL matching source, using default"); }
2363@NOLOG2@
2364  }
2365  return acl;
2366}
2367
2368#if __STDC__
2369char *sgAclAccess(struct Source *src, struct Acl *acl, struct SquidInfo *req)
2370#else
2371char *sgAclAccess(src, acl, req)
2372     struct Source *src;
2373     struct Acl *acl;
2374     struct SquidInfo *req;
2375#endif
2376{
2377  int access = 1,result;
2378  char *redirect = NULL, *dbdata = NULL, *p;
2379  struct sgRewrite *rewrite = NULL;
2380  struct AclDest *aclpass = NULL;
2381  if(acl == NULL)
2382    return NULL;
2383  if(acl->pass == NULL)
2384    acl->pass = defaultAcl->pass;
2385  if(acl->pass != NULL){
2386    for(aclpass = acl->pass; aclpass != NULL; aclpass = aclpass->next){
2387      if(aclpass->dest != NULL && !aclpass->dest->active)
2388	continue;
2389      if(aclpass->type == ACL_TYPE_TERMINATOR){
2390	access=aclpass->access;
2391	break;
2392      }
2393      if(aclpass->type == ACL_TYPE_INADDR){
2394	if(req->dot){
2395	  access=aclpass->access;
2396	  break;
2397	}
2398	continue;
2399      }
2400      if(aclpass->dest->domainlistDb != NULL){
2401	result = defined(aclpass->dest->domainlistDb, req->domain, &dbdata);
2402       if(result != DB_NOTFOUND) {
2403         if(result){
2404           if(aclpass->access){
2405             access++;
2406             break;
2407           } else {
2408             access = 0;
2409             break;
2410           }
2411	  }
2412	}
2413      else {
2414      }
2415      }
2416      if(aclpass->dest->urllistDb != NULL && access){
2417       result = defined(aclpass->dest->urllistDb,req->strippedurl, &dbdata);
2418       if (!result) {
2419         result = defined(aclpass->dest->urllistDb,req->furl, &dbdata);
2420       }
2421       if ((result) && (result != DB_NOTFOUND)) {
2422    if(aclpass->access){
2423      access++;
2424      break;
2425    } else {
2426      access = 0;
2427      break;
2428    }
2429  }
2430       else {
2431	}
2432      }
2433      if(aclpass->dest->regExp != NULL && access){
2434	if((result = sgRegExpMatch(aclpass->dest->regExp,req->strippedurl)) != 0){
2435	  if(aclpass->access){
2436	    access++;
2437	    break;
2438	  } else {
2439	    access = 0;
2440	    break;
2441	  }
2442	}
2443      }
2444    }
2445    if(!access){
2446      if(dbdata != NULL)
2447	redirect = dbdata;
2448      else if(aclpass->dest != NULL && aclpass->dest->redirect != NULL)
2449	redirect = aclpass->dest->redirect;
2450      else if(aclpass->dest != NULL && aclpass->dest->rewrite != NULL &&
2451	      (redirect =
2452	       sgRewriteExpression(aclpass->dest->rewrite,req->orig)) != NULL){
2453	;
2454      }
2455      else if(acl->redirect == NULL)
2456	redirect = defaultAcl->redirect;
2457      else
2458	redirect = acl->redirect;
2459    }
2460  } else {  /* acl->pass == NULL, probably defaultAcl->pass == NULL */
2461    access=0;
2462    redirect = defaultAcl->redirect;
2463  }
2464  if(acl->rewrite == NULL)
2465    rewrite = defaultAcl->rewrite;
2466  else
2467    rewrite = acl->rewrite;
2468  if(rewrite != NULL && access){
2469    if((p = sgRewriteExpression(rewrite,req->orig)) != NULL){
2470      redirect = p;
2471      if(rewrite->logfile != NULL){
2472	globalLogFile = rewrite->logfile;
2473       sgLogRequest(globalLogFile,req,acl,aclpass,rewrite,REQUEST_TYPE_REWRITE);
2474       return redirect;
2475      }
2476    }
2477  } else if(redirect != NULL) {
2478    redirect = sgParseRedirect(redirect, req, acl, aclpass);
2479  }
2480  if(src != NULL && src->logfile != NULL)
2481    globalLogFile = src->logfile;
2482  if(aclpass == NULL || aclpass->dest == NULL){
2483    if(defaultAcl->logfile != NULL)
2484     globalLogFile = defaultAcl->logfile;
2485  } else
2486    if(aclpass->dest->logfile != NULL)
2487      globalLogFile = aclpass->dest->logfile;
2488  if(globalLogFile != NULL) {
2489    if(redirect != NULL) {
2490      sgLogRequest(globalLogFile,req,acl,aclpass,NULL,REQUEST_TYPE_REDIRECT);
2491    } else {
2492      sgLogRequest(globalLogFile,req,acl,aclpass,NULL,REQUEST_TYPE_PASS);
2493    }
2494  }
2495  return redirect;
2496}
2497
2498#if __STDC__
2499void yyerror(char *s)
2500#else
2501void yyerror(s)
2502     char *s;
2503#endif
2504{
2505  sgLogFatalError("%s in configfile %s line %d",s,configFile,lineno);
2506}
2507
2508
2509#if __STDC__
2510int yywrap()
2511#else
2512int yywrap()
2513#endif
2514{
2515  return 1;
2516}
2517
2518/* returns 1 if user was found for the specified Source
2519 * returns a pointer to a UserInfo structure when found
2520 * handles all LDAP sub-lookups and caching
2521 */
2522#if __STDC__
2523int sgFindUser(struct Source *src, char *ident, struct UserInfo **rval)
2524#else
2525int sgFindUser(src, ident, rval)
2526       struct Source *src;
2527       char *ident;
2528       struct UserInfo **rval;
2529#endif
2530
2531{
2532       int i, found;
2533       int CacheTimeOut;
2534       char *interval;
2535       struct UserInfo *userinfo;
2536       static struct UserInfo info;
2537
2538       /* defined in the userDB? */
2539       if(defined(src->userDb, ident, (char **) &userinfo) == 1) {
2540#ifdef HAVE_LIBLDAP
2541               /* LDAP user? */
2542               if(!userinfo->ldapuser) {
2543                       *rval = userinfo;
2544                       return 1;       /* no, return regular user */
2545               }
2546
2547               /* from here on, we assume it is an LDAP user */
2548
2549               /* is this info valid? */
2550               interval = sgSettingGetValue("ldapcachetime");
2551               CacheTimeOut = atoi(interval != NULL ? interval : "0");
2552               if((time(NULL) - userinfo->cachetime) <= CacheTimeOut) {
2553                       if(userinfo->found)
2554                               *rval = userinfo;
2555                       return userinfo->found; /* yes */
2556               }
2557#endif
2558       }
2559       else {
2560               userinfo = NULL;        /* no record defined, must add our own*/
2561       }
2562
2563       found = 0;                      /* assume not found */
2564
2565#ifdef HAVE_LIBLDAP
2566       /* loop through all LDAP URLs and do a search */
2567       for(i = 0; i < src->ldapurlcount; i++) {
2568
2569               found = sgDoLdapSearch(src->ldapurls[i], ident);
2570
2571               /* cache every search in the user database */
2572               /* this should be safe, since squid only sends real idents
2573                  that have been authenticated (?) */
2574
2575               /* any record defined from above? */
2576               if(userinfo == NULL) {
2577                       /* no, must use our own memory */
2578                       userinfo = &info;
2579                       info.status = 0;
2580                       info.time = 0;
2581                       info.consumed = 0;
2582                       info.last = 0;
2583                       info.ldapuser = 1;
2584                       info.found = found;
2585                       info.cachetime = time(NULL);
2586               }
2587               else {
2588                       /* yes, just update the found flag */
2589                       userinfo->found = found;
2590                       userinfo->cachetime = time(NULL);
2591               }
2592
2593               sgDbUpdate(src->userDb, ident, (char *) userinfo,
2594                       sizeof(struct UserInfo));
2595               @NOLOG1@ sgLogError("Added LDAP source: %s", ident); @NOLOG2@
2596
2597               if(found) {
2598                       *rval = userinfo;
2599                       break;
2600               }
2601       }
2602#endif
2603       return found;
2604}
2605
2606#ifdef HAVE_LIBLDAP
2607
2608#if __STDC__
2609static int get_ldap_errno(LDAP *ld)
2610#else
2611static int get_ldap_errno(ld)
2612           LDAP *ld;
2613#endif
2614
2615{
2616  int err = 0;
2617  if(ld) {
2618    if(ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err) != LDAP_OPT_SUCCESS)
2619        err = 0;
2620  }
2621  return err;
2622}
2623
2624/*
2625 * expand_url - expand the %s codes in the given LDAP url
2626 *
2627 * Returns:  1 on success, 0 on error
2628 *
2629 *   char *expand;             destination buffer for expanded URL
2630 *   size_t expand_size;       size of dest buffer (sizeof() works here)
2631 *   char *url;                        original URL (MAXWORDLEN)
2632 *   char *s_item;             word to replace each occurance of %s with
2633 */
2634int expand_url(char *expand, size_t expand_size, const char *url,
2635              const char *s_item)
2636{
2637       int item_length;
2638       char *end = expand + expand_size;
2639
2640       item_length = strlen(s_item);
2641
2642       while (*url && expand < end) {
2643              if (url[0] == '%' && url[1] == 's') {
2644                       /* check buffer overrun */
2645                       if ((expand + item_length) >= end)
2646                               return 0;
2647                       strcpy(expand, s_item);
2648                       expand += item_length;
2649
2650                       url += 2;
2651               }
2652               else {
2653                       *expand++ = *url++;
2654               }
2655       }
2656
2657       if (expand < end) {
2658               *expand = '\0';         /* null terminate string */
2659               return 1;
2660       }
2661       else {
2662               return 0;
2663       }
2664}
2665
2666
2667/* does a raw LDAP search and returns 1 if found, 0 if not */
2668#if __STDC__
2669int sgDoLdapSearch(const char *url, const char *username)
2670#else
2671int sgDoLdapSearch(url, username)
2672       const char *url;
2673       const char *username;
2674#endif
2675{
2676       LDAPURLDesc *lud;
2677       LDAP *ld;
2678       LDAPMessage *ldapresult, *ldapentry;
2679       char *binddn = NULL, *bindpass = NULL;
2680       int ext_i;
2681       char **ldapvals;
2682       char buffer[MAX_BUF];
2683       int found = 0;
2684       int protoversion = -1;                  /* default to library defaults*/
2685       char *protosetting;
2686
2687       /* Which protocol version should we use? */
2688       protosetting = sgSettingGetValue("ldapprotover");
2689       if (protosetting != NULL) {
2690               if (atoi(protosetting) == 3) {
2691                       protoversion = LDAP_VERSION3;
2692               }
2693               else if (atoi(protosetting) == 2) {
2694                       protoversion = LDAP_VERSION2;
2695               }
2696       }
2697
2698       /* insert the username into the url, if needed... allow multiple %s */
2699       if (!expand_url(buffer, sizeof(buffer), url, username)) {
2700               sgLogError("%s: unable to expand LDAP URL: size: %u, username: "
2701                       "%s url: %s", progname, sizeof(buffer), username, url);
2702               return found;
2703       }
2704
2705       /* Parse RFC2255 LDAP URL */
2706       if(ldap_url_parse(buffer, &lud)) {
2707               sgLogError("%s: can't parse LDAP url %s",progname, buffer);
2708               return found;
2709       }
2710
2711       /* get a handle to an LDAP connection */
2712       if((ld = ldap_init(lud->lud_host, lud->lud_port)) == NULL) {
2713               sgLogError("%s: ldap_init(%s, %d) failed: %s", progname,
2714                       lud->lud_host, lud->lud_port, strerror(errno));
2715               ldap_free_urldesc(lud);
2716               return found;
2717       }
2718
2719       /* force an LDAP protocol version if set */
2720       if (protoversion != -1) {
2721               if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
2722                       &protoversion) != LDAP_OPT_SUCCESS)
2723               {
2724                       /* this will enter emergency mode */
2725                       sgLogFatalError("%s: ldap_set_option failed: %s",
2726                               progname, ldap_err2string(get_ldap_errno(ld)));
2727               }
2728       }
2729
2730       /*
2731        * Set binddn and bindpass with values from the config
2732        * file. Do this before the URL extentions so that they
2733        * override on a per-block basis.
2734        */
2735       binddn = sgSettingGetValue("ldapbinddn");
2736       bindpass = sgSettingGetValue("ldapbindpass");
2737
2738       /* check for supported URL extensions:
2739        *    bindname=<binddn>      (RFC2255)
2740        *    x-bindpass=<bindpass>  (user-specific, allowed by RFC2255)
2741        */
2742       for(ext_i = 0;
2743           lud->lud_exts != NULL && lud->lud_exts[ext_i] != NULL;
2744           ext_i++) {
2745
2746               char *key = lud->lud_exts[ext_i];
2747               char *data;
2748
2749               /* skip over any 'critical' markers */
2750               if (*key == '!')
2751                       key++;
2752
2753               /* find '=' sign (first one is all we care about) */
2754               data = strchr(key, '=');
2755               if (data == NULL)
2756                       continue;       /* invalid extension, skip */
2757               data++;             /* good extension, get data */
2758
2759               /* do we recognize the key? */
2760               if (strncmp(key, "bindname=", 9) == 0)
2761               {
2762                       binddn = data;
2763                       @NOLOG1@ sgLogError("Extracted binddn: %s", binddn); @NOLOG2@
2764               }
2765               else if (strncmp(key, "x-bindpass=", 11) == 0)
2766               {
2767                       bindpass = data;
2768                       @NOLOG1@ sgLogError("Extracted x-bindpass: %s", bindpass); @NOLOG2@
2769               }
2770       }
2771
2772       /* authenticate to the directory */
2773       if (ldap_simple_bind_s(ld, binddn, bindpass) != LDAP_SUCCESS) {
2774               sgLogError("%s: ldap_simple_bind_s failed: %s", progname,
2775               ldap_err2string(get_ldap_errno(ld)));
2776               ldap_unbind(ld);
2777               ldap_free_urldesc(lud);
2778               return found;
2779       }
2780
2781       /* Perform search */
2782       if(ldap_search_ext_s(ld, lud->lud_dn, lud->lud_scope, lud->lud_filter,
2783               lud->lud_attrs, 0, NULL, NULL, NULL, -1,
2784               &ldapresult) != LDAP_SUCCESS) {
2785
2786@NOLOG1@
2787               sgLogError("%s: ldap_search_ext_s failed: %s "
2788
2789                       "(params: %s, %d, %s, %s)",
2790                       progname, ldap_err2string(get_ldap_errno(ld)),
2791                       lud->lud_dn, lud->lud_scope, lud->lud_filter,
2792                       lud->lud_attrs[0]);
2793@NOLOG2@
2794
2795               ldap_unbind(ld);
2796               ldap_free_urldesc(lud);
2797               return found;
2798       }
2799
2800       /* return hash */
2801       ldapentry = ldap_first_entry(ld, ldapresult);
2802       if(ldapentry != NULL) {
2803               /* Use first attribute to get value */
2804               ldapvals = ldap_get_values(ld, ldapentry, lud->lud_attrs[0]);
2805               if(ldapvals != NULL) {
2806                       if(*ldapvals != NULL)
2807                               found = 1;
2808                       ldap_value_free(ldapvals);
2809               }
2810       }
2811
2812       /* cleanup */
2813       ldap_msgfree(ldapresult);
2814       ldap_unbind(ld);
2815       ldap_free_urldesc(lud);
2816       return found;
2817}
2818
2819#endif
2820