shared/cfgparser.c
b151ef55
 /*
5c4d94a9
  *  Copyright (C) 2002 - 2005 Tomasz Kojm <tkojm@clamav.net>
b151ef55
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
8b242bb9
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b151ef55
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
 #include "options.h"
36f2038b
 #include "cfgparser.h"
b151ef55
 #include "defaults.h"
e8217f5a
 #include "str.h"
36f2038b
 #include "memory.h"
a0e82bb5
 #include "misc.h"
b151ef55
 
5c4d94a9
 static int regcfg(struct cfgstruct **copt, char *optname, char *strarg, int numarg, short multiple);
 
 struct cfgstruct *getcfg(const char *cfgfile, int verbose)
b151ef55
 {
 	char buff[LINE_LENGTH], *name, *arg;
 	FILE *fs;
5c4d94a9
 	int line = 0, i, found, ctype, calc, val;
b151ef55
 	struct cfgstruct *copt = NULL;
 	struct cfgoption *pt;
 
 	struct cfgoption cfg_options[] = {
5c4d94a9
 	    {"LogFile",	OPT_STR, -1, NULL, 0},
 	    {"LogFileUnlock", OPT_BOOL, 0, NULL, 0},
 	    {"LogFileMaxSize", OPT_COMPSIZE, 1048576, NULL, 0},
 	    {"LogTime", OPT_BOOL, 0, NULL, 0},
 	    {"LogClean", OPT_BOOL, 0, NULL, 0},
 	    {"LogVerbose", OPT_BOOL, 0, NULL, 0}, /* clamd + freshclam */
 	    {"LogSyslog", OPT_BOOL, 0, NULL, 0},
 	    {"LogFacility", OPT_STR, -1, "LOG_LOCAL6", 0},
 	    {"PidFile", OPT_STR, -1, NULL, 0},
 	    {"TemporaryDirectory", OPT_STR, -1, NULL, 0},
 	    {"ScanPE", OPT_BOOL, 1, NULL, 0},
 	    {"DetectBrokenExecutables", OPT_BOOL, 0, NULL, 0},
 	    {"ScanMail", OPT_BOOL, 1, NULL, 0},
 	    {"MailFollowURLs", OPT_BOOL, 0, NULL, 0},
8eec8ae5
 	    {"DetectPhishing", OPT_BOOL, 1, NULL, 0},
95345f70
 	    {"ScanAlgo", OPT_BOOL, 1, NULL, 0},
5c4d94a9
 	    {"ScanHTML", OPT_BOOL, 1, NULL, 0},
 	    {"ScanOLE2", OPT_BOOL, 1, NULL, 0},
 	    {"ScanArchive", OPT_BOOL, 1, NULL, 0},
 	    {"ArchiveMaxFileSize", OPT_COMPSIZE, 10485760, NULL, 0},
 	    {"ArchiveMaxRecursion", OPT_NUM, 8, NULL, 0},
 	    {"ArchiveMaxFiles", OPT_NUM, 1000, NULL, 0},
 	    {"ArchiveMaxCompressionRatio", OPT_NUM, 250, NULL, 0},
 	    {"ArchiveLimitMemoryUsage", OPT_BOOL, 0, NULL, 0},
 	    {"ArchiveBlockEncrypted", OPT_BOOL, 0, NULL, 0},
 	    {"ArchiveBlockMax", OPT_BOOL, 0, NULL, 0},
 	    {"DatabaseDirectory", OPT_STR, -1, DATADIR, 0}, /* clamd + freshclam */
 	    {"TCPAddr", OPT_STR, -1, NULL, 0},
 	    {"TCPSocket", OPT_NUM, -1, NULL, 0},
 	    {"LocalSocket", OPT_STR, -1, NULL, 0},
 	    {"MaxConnectionQueueLength", OPT_NUM, 15, NULL, 0},
 	    {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, 0},
 	    {"StreamMinPort", OPT_NUM, 1024, NULL, 0},
 	    {"StreamMaxPort", OPT_NUM, 2048, NULL, 0},
 	    {"MaxThreads", OPT_NUM, 10, NULL, 0},
 	    {"ReadTimeout", OPT_NUM, 120, NULL, 0},
 	    {"IdleTimeout", OPT_NUM, 30, NULL, 0},
 	    {"MaxDirectoryRecursion", OPT_NUM, 15, NULL, 0},
 	    {"FollowDirectorySymlinks", OPT_BOOL, 0, NULL, 0},
 	    {"FollowFileSymlinks", OPT_BOOL, 0, NULL, 0},
 	    {"ExitOnOOM", OPT_BOOL, 0, NULL, 0},
 	    {"Foreground", OPT_BOOL, 0, NULL, 0}, /* clamd + freshclam */
 	    {"Debug", OPT_BOOL, 0, NULL, 0},
 	    {"LeaveTemporaryFiles", OPT_BOOL, 0, NULL, 0},
 	    {"FixStaleSocket", OPT_BOOL, 0, NULL, 0},
 	    {"User", OPT_STR, -1, NULL, 0},
 	    {"AllowSupplementaryGroups", OPT_BOOL, 0, NULL, 0},
 	    {"SelfCheck", OPT_NUM, 1800, NULL, 0},
 	    {"VirusEvent", OPT_FULLSTR, -1, NULL, 0},
 	    {"ClamukoScanOnAccess", OPT_BOOL, 0, NULL, 0},
 	    {"ClamukoScanOnOpen", OPT_BOOL, 0, NULL, 0},
 	    {"ClamukoScanOnClose", OPT_BOOL, 0, NULL, 0},
 	    {"ClamukoScanOnExec", OPT_BOOL, 0, NULL, 0},
 	    {"ClamukoIncludePath", OPT_STR, 0, NULL, 0},
 	    {"ClamukoExcludePath", OPT_STR, 0, NULL, 0},
 	    {"ClamukoMaxFileSize", OPT_COMPSIZE, 5242880, NULL, 0},
 	    {"ClamukoScanArchive", OPT_BOOL, 0, NULL, 0},
 	    {"DatabaseOwner", OPT_STR, -1, NULL, 0}, /* freshclam */
 	    {"Checks", OPT_NUM, 12, NULL, 0}, /* freshclam */
 	    {"UpdateLogFile", OPT_STR, -1, NULL, 0}, /* freshclam */
 	    {"DNSDatabaseInfo", OPT_STR, -1, "current.cvd.clamav.net", 0}, /* freshclam */
 	    {"DatabaseMirror", OPT_STR, -1, NULL, 1}, /* freshclam */
 	    {"MaxAttempts", OPT_NUM, 3, NULL, 0}, /* freshclam */
 	    {"HTTPProxyServer", OPT_STR, -1, NULL, 0}, /* freshclam */
 	    {"HTTPProxyPort", OPT_NUM, -1, NULL, 0}, /* freshclam */
 	    {"HTTPProxyUsername", OPT_STR, -1, NULL, 0}, /* freshclam */
 	    {"HTTPProxyPassword", OPT_STR, -1, NULL, 0}, /* freshclam */
db7875bc
 	    {"HTTPUserAgent", OPT_FULLSTR, -1, NULL, 0}, /* freshclam */
5c4d94a9
 	    {"NotifyClamd", OPT_STR, -1, NULL, 0}, /* freshclam */
 	    {"OnUpdateExecute", OPT_FULLSTR, -1, NULL, 0}, /* freshclam */
 	    {"OnErrorExecute", OPT_FULLSTR, -1, NULL, 0}, /* freshclam */
a6590a3d
 	    {"OnOutdatedExecute", OPT_FULLSTR, -1, NULL, 0}, /* freshclam */
5c4d94a9
 	    {"LocalIPAddress", OPT_STR, -1, NULL, 0}, /* freshclam */
0f47454e
 	    {0, 0, 0, 0, 0}
b151ef55
 	};
 
 
5c4d94a9
     for(i = 0; ; i++) {
 	pt = &cfg_options[i];
 	if(!pt->name)
 	    break;
 
 	if(regcfg(&copt, strdup(pt->name), pt->strarg ? strdup(pt->strarg) : NULL, pt->numarg, pt->multiple) < 0) {
 	    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 	    freecfg(copt);
 	    return NULL;
 	}
     }
 
     if((fs = fopen(cfgfile, "r")) == NULL) {
 	/* do not print error message here! */
 	freecfg(copt);
b151ef55
 	return NULL;
5c4d94a9
     }
b151ef55
 
     while(fgets(buff, LINE_LENGTH, fs)) {
 	line++;
 
 	if(buff[0] == '#')
 	    continue;
 
 	if(!strncmp("Example", buff, 7)) {
5c4d94a9
 	    if(verbose)
819c7c41
 		fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile);
25133aef
 	    fclose(fs);
5c4d94a9
 	    freecfg(copt);
b151ef55
 	    return NULL;
 	}
 
e8217f5a
 	if((name = cli_strtok(buff, 0, " \r\n"))) {
 	    arg = cli_strtok(buff, 1, " \r\n");
b151ef55
 	    found = 0;
 	    for(i = 0; ; i++) {
 		pt = &cfg_options[i];
 		if(pt->name) {
 		    if(!strcmp(name, pt->name)) {
 			found = 1;
 			switch(pt->argtype) {
 			    case OPT_STR:
 				if(!arg) {
5c4d94a9
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name);
25133aef
 				    fclose(fs);
5c4d94a9
 				    freecfg(copt);
 				    return NULL;
 				}
 				if(regcfg(&copt, name, arg, -1, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
 				    freecfg(copt);
b151ef55
 				    return NULL;
 				}
 				break;
c72178a4
 			    case OPT_FULLSTR:
5c4d94a9
 				/* an ugly hack of the above case */
c72178a4
 				if(!arg) {
5c4d94a9
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name);
25133aef
 				    fclose(fs);
5c4d94a9
 				    freecfg(copt);
c72178a4
 				    return NULL;
 				}
 				free(arg);
 				arg = strstr(buff, " ");
 				arg = strdup(++arg);
5c4d94a9
 				if(regcfg(&copt, name, arg, -1, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
 				    freecfg(copt);
 				    return NULL;
 				}
c72178a4
 				break;
b151ef55
 			    case OPT_NUM:
f5101389
 				if(!arg || !isnumb(arg)) {
5c4d94a9
 				    if(verbose)
819c7c41
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name);
25133aef
 				    fclose(fs);
5c4d94a9
 				    freecfg(copt);
 				    return NULL;
 				}
 				if(regcfg(&copt, name, NULL, atoi(arg), pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
 				    freecfg(copt);
 				    free(arg);
b151ef55
 				    return NULL;
 				}
e8217f5a
 				free(arg);
b151ef55
 				break;
 			    case OPT_COMPSIZE:
 				if(!arg) {
5c4d94a9
 				    if(verbose)
819c7c41
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name);
25133aef
 				    fclose(fs);
5c4d94a9
 				    freecfg(copt);
b151ef55
 				    return NULL;
 				}
 				ctype = tolower(arg[strlen(arg) - 1]);
 				if(ctype == 'm' || ctype == 'k') {
36f2038b
 				    char *cpy = (char *) mcalloc(strlen(arg), sizeof(char));
7cc3891c
 				    strncpy(cpy, arg, strlen(arg) - 1);
b151ef55
 				    if(!isnumb(cpy)) {
5c4d94a9
 					if(verbose)
819c7c41
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
25133aef
 					fclose(fs);
5c4d94a9
 					freecfg(copt);
b151ef55
 					return NULL;
 				    }
 				    if(ctype == 'm')
 					calc = atoi(cpy) * 1024 * 1024;
 				    else
 					calc = atoi(cpy) * 1024;
 				    free(cpy);
 				} else {
 				    if(!isnumb(arg)) {
5c4d94a9
 					if(verbose)
819c7c41
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
25133aef
 					fclose(fs);
5c4d94a9
 					freecfg(copt);
b151ef55
 					return NULL;
 				    }
 				    calc = atoi(arg);
 				}
e8217f5a
 				free(arg);
5c4d94a9
 				if(regcfg(&copt, name, NULL, calc, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
25133aef
 				    fclose(fs);
5c4d94a9
 				    freecfg(copt);
b151ef55
 				    return NULL;
 				}
 				break;
5c4d94a9
 			    case OPT_BOOL:
 
 				if(!arg) {
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name);
 				    fclose(fs);
 				    freecfg(copt);
 				    return NULL;
 				}
 
 				if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true")) {
 				    val = 1;
 				} else if(!strcasecmp(arg, "no") || !strcmp(arg, "0") || !strcasecmp(arg, "false")) {
 				    val = 0;
 				} else {
 				    if(verbose)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name);
 				    fclose(fs);
 				    freecfg(copt);
 				    return NULL;
 				}
 				free(arg);
 				if(regcfg(&copt, name, NULL, val, pt->multiple) < 0) {
 				    fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n");
 				    fclose(fs);
 				    freecfg(copt);
 				    return NULL;
 				}
b151ef55
 				break;
e8217f5a
 			    default:
5c4d94a9
 				if(verbose)
819c7c41
 				    fprintf(stderr, "ERROR: Parse error at line %d: Option %s is of unknown type %d\n", line, name, pt->argtype);
e8217f5a
 				free(name);
 				free(arg);
5c4d94a9
 				freecfg(copt);
 				return NULL;
b151ef55
 			}
 		    }
 		} else
 		    break;
5c4d94a9
 	    }
b151ef55
 
 	    if(!found) {
5c4d94a9
 		if(verbose)
819c7c41
 		    fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name);
25133aef
 		fclose(fs);
5c4d94a9
 		freecfg(copt);
b151ef55
 		return NULL;
 	    }
 	}
     }
 
     fclose(fs);
     return copt;
 }
 
9c1c9007
 void freecfg(struct cfgstruct *copt)
 {
     	struct cfgstruct *handler;
     	struct cfgstruct *arg;
 
5c4d94a9
     while(copt) {
9c1c9007
 	arg = copt->nextarg;
2fa47b44
 	while(arg) {
9c1c9007
 	    if(arg->strarg) {
 		free(arg->optname);
 		free(arg->strarg);
 		handler = arg;
2fa47b44
 		arg = arg->nextarg;
9c1c9007
 		free(handler);
2fa47b44
 	    } else
 		arg = arg->nextarg;
9c1c9007
 	}
5c4d94a9
 	if(copt->optname)
9c1c9007
 	    free(copt->optname);
5c4d94a9
 
 	if(copt->strarg)
9c1c9007
 	    free(copt->strarg);
5c4d94a9
 
9c1c9007
 	handler = copt;
 	copt = copt->next;
 	free(handler);
     }
     return;
 }
 
5c4d94a9
 struct cfgstruct *cfgopt(const struct cfgstruct *copt, const char *optname)
 {
 	struct cfgstruct *handler;
 
     handler = (struct cfgstruct *) copt;
 
     while(1) {
 	if(handler) {
 	    if(handler->optname)
 		if(!strcmp(handler->optname, optname))
 		    return handler;
 	} else break;
 	handler = handler->next;
     }
 
     return NULL;
 }
 
 static int regcfg(struct cfgstruct **copt, char *optname, char *strarg, int numarg, short multiple)
b151ef55
 {
 	struct cfgstruct *newnode, *pt;
 
5c4d94a9
 
b151ef55
     newnode = (struct cfgstruct *) mmalloc(sizeof(struct cfgstruct));
5c4d94a9
 
     if(!newnode)
 	return -1;
 
b151ef55
     newnode->optname = optname;
     newnode->nextarg = NULL;
     newnode->next = NULL;
5c4d94a9
     newnode->enabled = 0;
     newnode->multiple = multiple;
b151ef55
 
5c4d94a9
     if(strarg) {
b151ef55
 	newnode->strarg = strarg;
5c4d94a9
 	newnode->enabled = 1;
     } else {
b151ef55
 	newnode->strarg = NULL;
     }
 
5c4d94a9
     newnode->numarg = numarg;
     if(numarg != -1 && numarg != 0)
 	newnode->enabled = 1;
b151ef55
 
5c4d94a9
     if((pt = cfgopt(*copt, optname))) {
 	if(pt->multiple) {
b151ef55
 
5c4d94a9
 	    if(pt->enabled) {
 		while(pt->nextarg)
 		    pt = pt->nextarg;
b151ef55
 
5c4d94a9
 		pt->nextarg = newnode;
 	    } else {
 		pt->strarg = newnode->strarg;
 		pt->numarg = newnode->numarg;
 		pt->enabled = newnode->enabled;
 		free(newnode);
 	    }
 	    return 3; /* registered additional argument */
b151ef55
 
5c4d94a9
 	} else {
 	    pt->strarg = newnode->strarg;
 	    pt->numarg = newnode->numarg;
 	    pt->enabled = newnode->enabled;
 	    free(newnode);
 	    return 2;
 	}
b151ef55
 
5c4d94a9
     } else {
 	newnode->next = *copt;
 	*copt = newnode;
 	return 1;
     }
b151ef55
 }
5c4d94a9