shared/cfgparser.c
b151ef55
 /*
50099661
  *  Copyright (C) 2002 - 2004 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"
d2f07436
 #include "misc.h"
b151ef55
 
819c7c41
 struct cfgstruct *parsecfg(const char *cfgfile, int messages)
b151ef55
 {
 	char buff[LINE_LENGTH], *name, *arg;
 	FILE *fs;
 	int line = 0, i, found, ctype, calc;
 	struct cfgstruct *copt = NULL;
 	struct cfgoption *pt;
 
 	struct cfgoption cfg_options[] = {
 	    {"LogFile", OPT_STR},
 	    {"LogFileUnlock", OPT_NOARG},
 	    {"LogFileMaxSize", OPT_COMPSIZE},
 	    {"LogTime", OPT_NOARG},
50099661
 	    {"LogClean", OPT_NOARG},
0d98d74c
 	    {"LogVerbose", OPT_NOARG}, /* clamd + freshclam */
b151ef55
 	    {"LogSyslog", OPT_NOARG},
096e5bbd
 	    {"LogFacility", OPT_STR},
b151ef55
 	    {"PidFile", OPT_STR},
50099661
 	    {"TemporaryDirectory", OPT_STR},
511eef51
 	    {"DisableDefaultScanOptions", OPT_NOARG},
c2484690
 	    {"ScanPE", OPT_NOARG},
f8355d13
 	    {"DetectBrokenExecutables", OPT_NOARG},
b151ef55
 	    {"ScanMail", OPT_NOARG},
94da957a
 	    {"MailFollowURLs", OPT_NOARG},
2fe19b26
 	    {"ScanHTML", OPT_NOARG},
90e447be
 	    {"ScanOLE2", OPT_NOARG},
b151ef55
 	    {"ScanArchive", OPT_NOARG},
4cd4319e
 	    {"ScanRAR", OPT_NOARG},
b151ef55
 	    {"ArchiveMaxFileSize", OPT_COMPSIZE},
 	    {"ArchiveMaxRecursion", OPT_NUM},
 	    {"ArchiveMaxFiles", OPT_NUM},
cf899a29
 	    {"ArchiveMaxCompressionRatio", OPT_NUM},
b151ef55
 	    {"ArchiveLimitMemoryUsage", OPT_NOARG},
8373a9b0
 	    {"ArchiveBlockEncrypted", OPT_NOARG},
728f8802
 	    {"ArchiveBlockMax", OPT_NOARG},
0d98d74c
 	    {"DataDirectory", OPT_STR}, /* obsolete */
 	    {"DatabaseDirectory", OPT_STR}, /* clamd + freshclam */
4cd4319e
 	    {"TCPAddr", OPT_STR},
b151ef55
 	    {"TCPSocket", OPT_NUM},
 	    {"LocalSocket", OPT_STR},
 	    {"MaxConnectionQueueLength", OPT_NUM},
 	    {"StreamMaxLength", OPT_COMPSIZE},
166069c2
 	    {"StreamMinPort", OPT_NUM},
 	    {"StreamMaxPort", OPT_NUM},
b151ef55
 	    {"MaxThreads", OPT_NUM},
3520af97
 	    {"ReadTimeout", OPT_NUM},
a0231a19
 	    {"IdleTimeout", OPT_NUM},
b151ef55
 	    {"MaxDirectoryRecursion", OPT_NUM},
 	    {"FollowDirectorySymlinks", OPT_NOARG},
 	    {"FollowFileSymlinks", OPT_NOARG},
df4a42fe
 	    {"ExitOnOOM", OPT_NOARG},
55ae06a5
 	    {"Foreground", OPT_NOARG}, /* clamd + freshclam */
c72178a4
 	    {"Debug", OPT_NOARG},
3506c157
 	    {"LeaveTemporaryFiles", OPT_NOARG},
c6259ac5
 	    {"FixStaleSocket", OPT_NOARG},
b151ef55
 	    {"User", OPT_STR},
 	    {"AllowSupplementaryGroups", OPT_NOARG},
 	    {"SelfCheck", OPT_NUM},
c72178a4
 	    {"VirusEvent", OPT_FULLSTR},
b5ad6489
 	    {"ClamukoScanOnLine", OPT_NOARG}, /* old name */
 	    {"ClamukoScanOnAccess", OPT_NOARG},
b151ef55
 	    {"ClamukoScanOnOpen", OPT_NOARG},
 	    {"ClamukoScanOnClose", OPT_NOARG},
 	    {"ClamukoScanOnExec", OPT_NOARG},
 	    {"ClamukoIncludePath", OPT_STR},
 	    {"ClamukoExcludePath", OPT_STR},
 	    {"ClamukoMaxFileSize", OPT_COMPSIZE},
 	    {"ClamukoScanArchive", OPT_NOARG},
0d98d74c
 	    {"DatabaseOwner", OPT_STR}, /* freshclam */
 	    {"Checks", OPT_NUM}, /* freshclam */
 	    {"UpdateLogFile", OPT_STR}, /* freshclam */
881069d7
 	    {"DNSDatabaseInfo", OPT_STR}, /* freshclam */
0d98d74c
 	    {"DatabaseMirror", OPT_STR}, /* freshclam */
 	    {"MaxAttempts", OPT_NUM}, /* freshclam */
 	    {"HTTPProxyServer", OPT_STR}, /* freshclam */
d11fb38a
 	    {"HTTPProxyPort", OPT_NUM}, /* freshclam */
819bbe1f
 	    {"HTTPProxyUsername", OPT_STR}, /* freshclam */
0d98d74c
 	    {"HTTPProxyPassword", OPT_STR}, /* freshclam */
 	    {"NotifyClamd", OPT_OPTARG}, /* freshclam */
 	    {"OnUpdateExecute", OPT_FULLSTR}, /* freshclam */
 	    {"OnErrorExecute", OPT_FULLSTR}, /* freshclam */
0539b2a8
 	    {"LocalIPAddress", OPT_STR}, /* freshclam */
b151ef55
 	    {0, 0}
 	};
 
 
25133aef
     if((fs = fopen(cfgfile, "r")) == NULL)
b151ef55
 	return NULL;
 
     while(fgets(buff, LINE_LENGTH, fs)) {
 
 	line++;
 
 	if(buff[0] == '#')
 	    continue;
 
 	if(!strncmp("Example", buff, 7)) {
819c7c41
 	    if(messages)
 		fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile);
25133aef
 	    fclose(fs);
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) {
819c7c41
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string as argument.\n", line, name);
25133aef
 				    fclose(fs);
b151ef55
 				    return NULL;
 				}
 				copt = regcfg(copt, name, arg, 0);
 				break;
c72178a4
 			    case OPT_FULLSTR:
 				if(!arg) {
819c7c41
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string as argument.\n", line, name);
25133aef
 				    fclose(fs);
c72178a4
 				    return NULL;
 				}
8a05efc5
 				/* FIXME: this one is an ugly hack of the above case */
c72178a4
 				free(arg);
 				arg = strstr(buff, " ");
 				arg = strdup(++arg);
 				copt = regcfg(copt, name, arg, 0);
 				break;
b151ef55
 			    case OPT_NUM:
f5101389
 				if(!arg || !isnumb(arg)) {
819c7c41
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name);
25133aef
 				    fclose(fs);
b151ef55
 				    return NULL;
 				}
 				copt = regcfg(copt, name, NULL, atoi(arg));
e8217f5a
 				free(arg);
b151ef55
 				break;
 			    case OPT_COMPSIZE:
 				if(!arg) {
819c7c41
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name);
25133aef
 				    fclose(fs);
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)) {
819c7c41
 					if(messages)
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
25133aef
 					fclose(fs);
b151ef55
 					return NULL;
 				    }
 				    if(ctype == 'm')
 					calc = atoi(cpy) * 1024 * 1024;
 				    else
 					calc = atoi(cpy) * 1024;
 				    free(cpy);
 				} else {
 				    if(!isnumb(arg)) {
819c7c41
 					if(messages)
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
25133aef
 					fclose(fs);
b151ef55
 					return NULL;
 				    }
 				    calc = atoi(arg);
 				}
 				copt = regcfg(copt, name, NULL, calc);
e8217f5a
 				free(arg);
b151ef55
 				break;
 			    case OPT_NOARG:
 				if(arg) {
819c7c41
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s doesn't support arguments (got '%s').\n", line, name, arg);
25133aef
 				    fclose(fs);
b151ef55
 				    return NULL;
 				}
 				copt = regcfg(copt, name, NULL, 0);
 				break;
 			    case OPT_OPTARG:
 				copt = regcfg(copt, name, arg, 0);
 				break;
e8217f5a
 			    default:
819c7c41
 				if(messages)
 				    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);
 				break;
b151ef55
 			}
 		    }
 		} else
 		    break;
 	    } 
 
 	    if(!found) {
819c7c41
 		if(messages)
 		    fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name);
25133aef
 		fclose(fs);
b151ef55
 		return NULL;
 	    }
 	}
     }
 
     fclose(fs);
     return copt;
 }
 
9c1c9007
 void freecfg(struct cfgstruct *copt)
 {
     	struct cfgstruct *handler;
     	struct cfgstruct *arg;
 
     while (copt) {
 	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
 	}
 	if(copt->optname) {
 	    free(copt->optname);
 	}
 	if(copt->strarg) {
 	    free(copt->strarg);
 	}
 	handler = copt;
 	copt = copt->next;
 	free(handler);
     }
     return;
 }
 
 struct cfgstruct *regcfg(struct cfgstruct *copt, char *optname, char *strarg, int numarg)
b151ef55
 {
 	struct cfgstruct *newnode, *pt;
 
     newnode = (struct cfgstruct *) mmalloc(sizeof(struct cfgstruct));
     newnode->optname = optname;
     newnode->nextarg = NULL;
     newnode->next = NULL;
 
     if(strarg)
 	newnode->strarg = strarg;
     else {
 	newnode->strarg = NULL;
 	newnode->numarg = numarg;
     }
 
     if((pt = cfgopt(copt, optname))) {
 	while(pt->nextarg)
 	    pt = pt->nextarg;
 
 	pt->nextarg = newnode;
 	return copt;
     } else {
 	newnode->next = copt;
 	return newnode;
     }
 }
 
 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;
 }