clamav-devel/shared/cfgparser.c
e3aaff8e
 /*
ee039e40
  *  Copyright (C) 2002 - 2004 Tomasz Kojm <tkojm@clamav.net>
e3aaff8e
  *
  *  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.
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
 #include "options.h"
afb48b28
 #include "cfgparser.h"
e3aaff8e
 #include "defaults.h"
2d70a403
 #include "str.h"
afb48b28
 #include "memory.h"
 
 static int isnumb(const char *str)
 {
 	int i;
 
     for(i = 0; i < strlen(str); i++)
 	if(!isdigit(str[i]))
 	    return 0;
 
     return 1;
 }
e3aaff8e
 
cc71d7c2
 struct cfgstruct *parsecfg(const char *cfgfile, int messages)
e3aaff8e
 {
 	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},
ee039e40
 	    {"LogClean", OPT_NOARG},
95d401c4
 	    {"LogVerbose", OPT_NOARG}, /* clamd + freshclam */
e3aaff8e
 	    {"LogSyslog", OPT_NOARG},
c695dab4
 	    {"LogFacility", OPT_STR},
e3aaff8e
 	    {"PidFile", OPT_STR},
ee039e40
 	    {"TemporaryDirectory", OPT_STR},
e3aaff8e
 	    {"MaxFileSize", OPT_COMPSIZE},
a9082ea2
 	    {"ScanPE", OPT_NOARG},
20c3d44d
 	    {"DetectBrokenExecutables", OPT_NOARG},
e3aaff8e
 	    {"ScanMail", OPT_NOARG},
a36e6e5c
 	    {"MailFollowURLs", OPT_NOARG},
888f5794
 	    {"ScanHTML", OPT_NOARG},
e8c9ccdb
 	    {"ScanOLE2", OPT_NOARG},
e3aaff8e
 	    {"ScanArchive", OPT_NOARG},
8139fd99
 	    {"ScanRAR", OPT_NOARG},
e3aaff8e
 	    {"ArchiveMaxFileSize", OPT_COMPSIZE},
 	    {"ArchiveMaxRecursion", OPT_NUM},
 	    {"ArchiveMaxFiles", OPT_NUM},
a6945b5d
 	    {"ArchiveMaxCompressionRatio", OPT_NUM},
e3aaff8e
 	    {"ArchiveLimitMemoryUsage", OPT_NOARG},
5484e03c
 	    {"ArchiveBlockEncrypted", OPT_NOARG},
95d401c4
 	    {"DataDirectory", OPT_STR}, /* obsolete */
 	    {"DatabaseDirectory", OPT_STR}, /* clamd + freshclam */
8139fd99
 	    {"TCPAddr", OPT_STR},
e3aaff8e
 	    {"TCPSocket", OPT_NUM},
 	    {"LocalSocket", OPT_STR},
 	    {"MaxConnectionQueueLength", OPT_NUM},
 	    {"StreamSaveToDisk", OPT_NOARG},
 	    {"StreamMaxLength", OPT_COMPSIZE},
 	    {"MaxThreads", OPT_NUM},
7390dfcd
 	    {"ReadTimeout", OPT_NUM},
e3aaff8e
 	    {"MaxDirectoryRecursion", OPT_NUM},
 	    {"FollowDirectorySymlinks", OPT_NOARG},
 	    {"FollowFileSymlinks", OPT_NOARG},
 	    {"Foreground", OPT_NOARG},
0249f9d2
 	    {"Debug", OPT_NOARG},
590135f9
 	    {"LeaveTemporaryFiles", OPT_NOARG},
049a18b9
 	    {"FixStaleSocket", OPT_NOARG},
e3aaff8e
 	    {"User", OPT_STR},
 	    {"AllowSupplementaryGroups", OPT_NOARG},
 	    {"SelfCheck", OPT_NUM},
0249f9d2
 	    {"VirusEvent", OPT_FULLSTR},
2b278a02
 	    {"ClamukoScanOnLine", OPT_NOARG}, /* old name */
 	    {"ClamukoScanOnAccess", OPT_NOARG},
e3aaff8e
 	    {"ClamukoScanOnOpen", OPT_NOARG},
 	    {"ClamukoScanOnClose", OPT_NOARG},
 	    {"ClamukoScanOnExec", OPT_NOARG},
 	    {"ClamukoIncludePath", OPT_STR},
 	    {"ClamukoExcludePath", OPT_STR},
 	    {"ClamukoMaxFileSize", OPT_COMPSIZE},
 	    {"ClamukoScanArchive", OPT_NOARG},
95d401c4
 	    {"DatabaseOwner", OPT_STR}, /* freshclam */
 	    {"Checks", OPT_NUM}, /* freshclam */
 	    {"UpdateLogFile", OPT_STR}, /* freshclam */
 	    {"DatabaseMirror", OPT_STR}, /* freshclam */
 	    {"MaxAttempts", OPT_NUM}, /* freshclam */
 	    {"HTTPProxyServer", OPT_STR}, /* freshclam */
8105f365
 	    {"HTTPProxyPort", OPT_NUM}, /* freshclam */
a7d9bef2
 	    {"HTTPProxyUsername", OPT_STR}, /* freshclam */
95d401c4
 	    {"HTTPProxyPassword", OPT_STR}, /* freshclam */
 	    {"NotifyClamd", OPT_OPTARG}, /* freshclam */
 	    {"OnUpdateExecute", OPT_FULLSTR}, /* freshclam */
 	    {"OnErrorExecute", OPT_FULLSTR}, /* freshclam */
e3aaff8e
 	    {0, 0}
 	};
 
 
     if((fs = fopen(cfgfile, "r")) == NULL) {
 	return NULL;
     }
 
 
     while(fgets(buff, LINE_LENGTH, fs)) {
 
 	line++;
 
 	if(buff[0] == '#')
 	    continue;
 
 	if(!strncmp("Example", buff, 7)) {
cc71d7c2
 	    if(messages)
 		fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile);
e3aaff8e
 	    return NULL;
 	}
 
 
2d70a403
 	if((name = cli_strtok(buff, 0, " \r\n"))) {
 	    arg = cli_strtok(buff, 1, " \r\n");
e3aaff8e
 	    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) {
cc71d7c2
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string as argument.\n", line, name);
e3aaff8e
 				    return NULL;
 				}
 				copt = regcfg(copt, name, arg, 0);
 				break;
0249f9d2
 			    case OPT_FULLSTR:
 				if(!arg) {
cc71d7c2
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string as argument.\n", line, name);
0249f9d2
 				    return NULL;
 				}
bf5a1ce7
 				/* FIXME: this one is an ugly hack of the above case */
0249f9d2
 				free(arg);
 				arg = strstr(buff, " ");
 				arg = strdup(++arg);
 				copt = regcfg(copt, name, arg, 0);
 				break;
e3aaff8e
 			    case OPT_NUM:
0deebced
 				if(!arg || !isnumb(arg)) {
cc71d7c2
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name);
e3aaff8e
 				    return NULL;
 				}
 				copt = regcfg(copt, name, NULL, atoi(arg));
2d70a403
 				free(arg);
e3aaff8e
 				break;
 			    case OPT_COMPSIZE:
 				if(!arg) {
cc71d7c2
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name);
e3aaff8e
 				    return NULL;
 				}
 				ctype = tolower(arg[strlen(arg) - 1]);
 				if(ctype == 'm' || ctype == 'k') {
afb48b28
 				    char *cpy = (char *) mcalloc(strlen(arg), sizeof(char));
658f19f8
 				    strncpy(cpy, arg, strlen(arg) - 1);
e3aaff8e
 				    if(!isnumb(cpy)) {
cc71d7c2
 					if(messages)
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
e3aaff8e
 					return NULL;
 				    }
 				    if(ctype == 'm')
 					calc = atoi(cpy) * 1024 * 1024;
 				    else
 					calc = atoi(cpy) * 1024;
 				    free(cpy);
 				} else {
 				    if(!isnumb(arg)) {
cc71d7c2
 					if(messages)
 					    fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name);
e3aaff8e
 					return NULL;
 				    }
 				    calc = atoi(arg);
 				}
 				copt = regcfg(copt, name, NULL, calc);
2d70a403
 				free(arg);
e3aaff8e
 				break;
 			    case OPT_NOARG:
 				if(arg) {
cc71d7c2
 				    if(messages)
 					fprintf(stderr, "ERROR: Parse error at line %d: Option %s doesn't support arguments (got '%s').\n", line, name, arg);
e3aaff8e
 				    return NULL;
 				}
 				copt = regcfg(copt, name, NULL, 0);
 				break;
 			    case OPT_OPTARG:
 				copt = regcfg(copt, name, arg, 0);
 				break;
2d70a403
 			    default:
cc71d7c2
 				if(messages)
 				    fprintf(stderr, "ERROR: Parse error at line %d: Option %s is of unknown type %d\n", line, name, pt->argtype);
2d70a403
 				free(name);
 				free(arg);
 				break;
e3aaff8e
 			}
 		    }
 		} else
 		    break;
 	    } 
 
 	    if(!found) {
cc71d7c2
 		if(messages)
 		    fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name);
e3aaff8e
 		return NULL;
 	    }
 	}
     }
 
     fclose(fs);
     return copt;
 }
 
9e431a95
 void freecfg(struct cfgstruct *copt)
 {
     	struct cfgstruct *handler;
     	struct cfgstruct *arg;
 
     while (copt) {
 	arg = copt->nextarg;
5467c418
 	while(arg) {
9e431a95
 	    if(arg->strarg) {
 		free(arg->optname);
 		free(arg->strarg);
 		handler = arg;
5467c418
 		arg = arg->nextarg;
9e431a95
 		free(handler);
5467c418
 	    } else
 		arg = arg->nextarg;
9e431a95
 	}
 	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)
e3aaff8e
 {
 	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;
 }