home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Magazine
/
wwwoffle-2.1.tar.gz
/
wwwoffle-2.1
/
config.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-02-20
|
61KB
|
2,151 lines
/***************************************
$Header: /home/amb/wwwoffle/RCS/config.c 2.21 1998/02/20 20:15:41 amb Exp $
WWWOFFLE - World Wide Web Offline Explorer - Version 2.1.
Configuration file management functions.
******************/ /******************
Written by Andrew M. Bishop
This file Copyright 1997,98 Andrew M. Bishop
It may be distributed under the GNU Public License, version 2, or
any higher version. See section COPYING of the GNU Public license
for conditions under which this file may be redistributed.
***************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include "config.h"
#include "misc.h"
#include "proto.h"
#include "sockets.h"
#include "errors.h"
#include "wwwoffle.h"
#ifndef SPOOL_DIR
#define SPOOL_DIR DEF_SPOOL
#endif
#ifndef CONF_DIR
#define CONF_DIR DEF_CONF
#endif
/* For upgrade-cache and wwwoffle-tools we don't need to read the config file
we just need some variables so we keep this SIMPLE. */
#ifndef SIMPLE
/* Type definitions */
/*+ The type of value to expect for a value. +*/
typedef enum _ConfigType
{
Fixed, /*+ When the left hand side is fixed. +*/
None, /*+ When there is no right hand side. +*/
Boolean, /*+ A boolean response (yes/no 1/0 true/false). +*/
PortNumber, /*+ For port numbers (>0). +*/
AgeDays, /*+ An age in days (can be -ve). +*/
TimeSecs, /*+ An time in seconds (can be -ve). +*/
FileSize, /*+ A file size (must be >=0). +*/
CfgMaxServers, /*+ Max number of servers to fork (>0, <MAX_SERVERS). +*/
CfgMaxFetchServers, /*+ Max number of servers for fetching pages (>0, <MAX_FETCH_SERVERS). +*/
CfgLogLevel, /*+ A log level (debug, info, important, warning or fatal). +*/
UserId, /*+ For user IDs, (numeric or string). +*/
GroupId, /*+ For group IDs, (numeric or string). +*/
String, /*+ For an arbitrary string (no whitespace). +*/
DirName, /*+ For directory name values (string starting with '/'). +*/
PathOrFileExt, /*+ A path (/string) or file extension (.string). +*/
MIMEType, /*+ A MIME type (string/string). +*/
Host, /*+ For host names (string). +*/
HostAndPort, /*+ For host name and port numbers (string[:port]). +*/
HostAndPortOrNone, /*+ For host name and port numbers (string[:port]) or nothing. +*/
ProtoAndHost, /*+ For protocols and host names (string/string). +*/
ProtoAndHostOrDefault /*+ For protocols and host names (string/string) or default. +*/
}
ConfigType;
/*+ A description of an entry in a section of the config file. +*/
typedef struct _Entry
{
char *name; /*+ The name of the entry. +*/
void *pointer; /*+ A pointer to the value of the entry. +*/
char list; /*+ Set to true if it is a list of KeyPairs. +*/
ConfigType left_type; /*+ The type of the left side of the equals sign. +*/
ConfigType right_type; /*+ The type of the right side of the equals sign. +*/
}
Entry;
/*+ A description of a section in the config file. +*/
typedef struct _Section
{
char *name; /*+ The name of the section. +*/
Entry *entries; /*+ The entries in the section (NULL terminated). +*/
}
Section;
/*+ A keyed entry for a wildcard left hand side. +*/
typedef struct _KeyPair
{
char *name; /*+ The key name of the entry. +*/
union
{
int integer; /*+ An integer value. +*/
char *string; /*+ A string value. +*/
}
value;
}
KeyPair;
/*+ The last of a KeyPair list. +*/
static KeyPair KeyPairEnd={NULL};
#endif /* SIMPLE */
/* StartUp section */
/*+ The name of the configuration file. +*/
char *ConfigFile=CONF_DIR "/wwwoffle.conf";
/*+ The port number to use for the HTTP proxy port. +*/
int HTTP_Port=DEF_HTTP_PORT;
/*+ The port number to use for the wwwoffle port. +*/
int WWWOFFLE_Port=DEF_WWWOFFLE_PORT;
/*+ The spool directory. +*/
char *SpoolDir=SPOOL_DIR;
/*+ The user id for wwwoffled or -1 for none. +*/
int WWWOFFLE_Uid=-1;
/*+ The group id for wwwoffled or -1 for none. +*/
int WWWOFFLE_Gid=-1;
/*+ Whether to use the syslog facility or not. +*/
int UseSyslog=1;
/*+ The password required for demon configuration. +*/
char *PassWord=NULL;
#ifndef SIMPLE
/*+ Maximum number of servers +*/
int MaxServers=DEF_MAX_SERVERS; /*+ in total. +*/
int MaxFetchServers=DEF_MAX_FETCH_SERVERS; /*+ for fetching. +*/
/*+ The entries in the StartUp section. +*/
static Entry startup_entries[]={{"http-port" ,(void*)&HTTP_Port ,0,Fixed,PortNumber },
{"wwwoffle-port" ,(void*)&WWWOFFLE_Port ,0,Fixed,PortNumber },
{"spool-dir" ,(void*)&SpoolDir ,0,Fixed,DirName },
{"run-uid" ,(void*)&WWWOFFLE_Uid ,0,Fixed,UserId },
{"run-gid" ,(void*)&WWWOFFLE_Gid ,0,Fixed,GroupId },
{"use-syslog" ,(void*)&UseSyslog ,0,Fixed,Boolean },
{"password" ,(void*)&PassWord ,0,Fixed,String },
{"max-servers" ,(void*)&MaxServers ,0,Fixed,CfgMaxServers },
{"max-fetch-servers",(void*)&MaxFetchServers ,0,Fixed,CfgMaxFetchServers},
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The StartUp section. +*/
static Section startup_section={"StartUp",startup_entries};
#endif /* SIMPLE */
/* Options Section */
/*+ The level of error logging (see ErrorLevel in errors.h) +*/
int LogLevel=Important, /*+ in the config file for syslog and stderr. +*/ /* see also SetDefaultValues() */
DebugLevel=-1; /*+ on the command line for stderr. +*/ /* not in the config file */
#ifndef SIMPLE
/*+ The option to also fetch images. +*/
int FetchImages; /* see SetDefaultValues() */
/*+ The option to also fetch frames. +*/
int FetchFrames; /* see SetDefaultValues() */
/*+ The number of days to display in the index of the latest pages. +*/
int IndexLatestDays; /* see SetDefaultValues() */
/*+ The option of a tag that can be added to the bottom of the spooled pages with the date and a refresh button. +*/
int AddInfoRefresh; /* see SetDefaultValues() */
/*+ The maximum age of a cached page to use in preference while online. +*/
int RequestChanged; /* see SetDefaultValues() */
/*+ The option to allow or ignore the 'Pragma: no-cache' request. +*/
int PragmaNoCache; /* see SetDefaultValues() */
/*+ The option to not make requests while offline but to return an error. +*/
int OfflineRequests; /* see SetDefaultValues() */
/*+ The interval in days between monitoring the specified pages. +*/
int MonitorInterval; /* see SetDefaultValues() */
/*+ The entries in the Options section. +*/
static Entry options_entries[]={{"log-level" ,(void*)&LogLevel ,0,Fixed,CfgLogLevel},
{"fetch-images" ,(void*)&FetchImages ,0,Fixed,Boolean },
{"fetch-frames" ,(void*)&FetchFrames ,0,Fixed,Boolean },
{"index-latest-days",(void*)&IndexLatestDays,0,Fixed,AgeDays },
{"add-info-refresh" ,(void*)&AddInfoRefresh ,0,Fixed,Boolean },
{"request-changed" ,(void*)&RequestChanged ,0,Fixed,TimeSecs },
{"pragma-no-cache" ,(void*)&PragmaNoCache ,0,Fixed,Boolean },
{"offline-requests" ,(void*)&OfflineRequests,0,Fixed,Boolean },
{"monitor-interval" ,(void*)&MonitorInterval,0,Fixed,AgeDays },
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The Options section. +*/
static Section options_section={"Options",options_entries};
/* LocalHost section */
/*+ The list of localhost hostnames. +*/
static KeyPair **LocalHost; /* see SetDefaultValues() */
/*+ The entries in the LocalHost section. +*/
static Entry localhost_entries[]={{"" ,(void*)&LocalHost,1,Host,None},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The LocalHost section. +*/
static Section localhost_section={"LocalHost",localhost_entries};
/* LocalNet section */
/*+ The list of local network hostnames. +*/
static KeyPair **LocalNet; /* see SetDefaultValues() */
/*+ The entries in the LocalNet section. +*/
static Entry localnet_entries[]={{"" ,(void*)&LocalNet,1,Host,None},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The LocalNet section. +*/
static Section localnet_section={"LocalNet",localnet_entries};
/* AllowedConnect section */
/*+ The list of allowed hostnames. +*/
static KeyPair **AllowedConnect; /* see SetDefaultValues() */
/*+ The entries in the AllowedConnect section. +*/
static Entry allowedconnect_entries[]={{"" ,(void*)&AllowedConnect,1,Host,None},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The AllowedConnect section. +*/
static Section allowedconnect_section={"AllowedConnect",allowedconnect_entries};
/* DontCache section */
/*+ The list of servers and reasons not to cache. +*/
static KeyPair **DontCache; /* see SetDefaultValues() */
/*+ The entries in the DontCache section. +*/
static Entry dontcache_entries[]={{"" ,(void*)&DontCache,1,ProtoAndHostOrDefault,PathOrFileExt},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The DontCache section. +*/
static Section dontcache_section={"DontCache",dontcache_entries};
/* DontGet section */
/*+ The list of servers and reasons not to get. +*/
static KeyPair **DontGet; /* see SetDefaultValues() */
/*+ The entries in the DontGet section. +*/
static Entry dontget_entries[]={{"" ,(void*)&DontGet,1,ProtoAndHostOrDefault,PathOrFileExt},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The DontGet section. +*/
static Section dontget_section={"DontGet",dontget_entries};
/* DontGetRecursive section */
/*+ The list of servers and reasons not to get. +*/
static KeyPair **DontGetRecursive; /* see SetDefaultValues() */
/*+ The entries in the DontGetRecursive section. +*/
static Entry dontgetrecursive_entries[]={{"" ,(void*)&DontGetRecursive,1,ProtoAndHostOrDefault,PathOrFileExt},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The DontGetRecursive section. +*/
static Section dontgetrecursive_section={"DontGetRecursive",dontgetrecursive_entries};
/* CensorHeader section */
/*+ The list of censored headers. +*/
static KeyPair **CensorHeader; /* see SetDefaultValues() */
/*+ The entries in the censor headers section. +*/
static Entry censorheader_entries[]={{"" ,(void*)&CensorHeader,1,String,None},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The CensorHeader section. +*/
static Section censorheader_section={"CensorHeader",censorheader_entries};
/* FTPOptions section */
/*+ The anon-ftp username. +*/
static char *FTPUserName; /* see SetDefaultValues() */
/*+ The anon-ftp password. +*/
static char *FTPPassWord; /* see SetDefaultValues() */
/*+ The information that is needed to allow non-anonymous access, +*/
static KeyPair **FTPAuthHost, /*+ hostname +*/ /* see SetDefaultValues() */
**FTPAuthUser, /*+ username +*/ /* see SetDefaultValues() */
**FTPAuthPass; /*+ password +*/ /* see SetDefaultValues() */
/*+ The entries in the FTPOptions section. +*/
static Entry ftpoptions_entries[]={{"anon-username",(void*)&FTPUserName,0,Fixed,String },
{"anon-password",(void*)&FTPPassWord,0,Fixed,String },
{"auth-hostname",(void*)&FTPAuthHost,1,Fixed,HostAndPort},
{"auth-username",(void*)&FTPAuthUser,1,Fixed,String },
{"auth-password",(void*)&FTPAuthPass,1,Fixed,String },
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The FTPOptions section. +*/
static Section ftpoptions_section={"FTPOptions",ftpoptions_entries};
/* MIMETypes section */
/*+ The default MIME type. +*/
static char *DefaultMIMEType; /* see SetDefaultValues() */
/*+ The list of MIME types. +*/
static KeyPair **MIMETypes; /* see SetDefaultValues() */
/*+ The entries in the FTPOptions section. +*/
static Entry mimetypes_entries[]={{"default",(void*)&DefaultMIMEType,0,Fixed ,MIMEType},
{"" ,(void*)&MIMETypes ,1,PathOrFileExt,MIMEType},
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The MIMETypes section. +*/
static Section mimetypes_section={"MIMETypes",mimetypes_entries};
/* Proxy section */
/*+ The list of hostnames and proxies. +*/
static KeyPair **Proxies; /* see SetDefaultValues() */
/*+ The information that is needed to allow authorisation headers to be added, +*/
static KeyPair **ProxyAuthHost, /*+ hostname +*/ /* see SetDefaultValues() */
**ProxyAuthUser, /*+ username +*/ /* see SetDefaultValues() */
**ProxyAuthPass; /*+ password +*/ /* see SetDefaultValues() */
/*+ The entries in the Proxy section. +*/
static Entry proxy_entries[]={{"auth-hostname",(void*)&ProxyAuthHost,1,Fixed ,HostAndPort },
{"auth-username",(void*)&ProxyAuthUser,1,Fixed ,String },
{"auth-password",(void*)&ProxyAuthPass,1,Fixed ,String },
{"" ,(void*)&Proxies ,1,ProtoAndHostOrDefault,HostAndPortOrNone},
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The Proxy section. +*/
static Section proxy_section={"Proxy",proxy_entries};
/* Mirror section */
/*+ The list of protocols/hostnames and their mirrors. +*/
static KeyPair **Mirrors; /* see SetDefaultValues() */
/*+ The entries in the Purge section. +*/
static Entry mirror_entries[]={{"" ,(void*)&Mirrors,1,ProtoAndHost,ProtoAndHost},
{NULL,NULL ,0,-1 ,-1 }};
/*+ The Mirror section. +*/
static Section mirror_section={"Mirror",mirror_entries};
/* Purge section */
/*+ If true then use modification time instead of access time. +*/
int PurgeUseMTime; /* see SetDefaultValues() */
/*+ The default age for purging files. +*/
int DefaultPurgeAge; /* see SetDefaultValues() */
/*+ The maximum allowed size of the cache. +*/
int PurgeCacheSize; /* see SetDefaultValues() */
/*+ The list of hostnames and purge ages. +*/
static KeyPair **PurgeAges; /* see SetDefaultValues() */
/*+ The entries in the Purge section. +*/
static Entry purge_entries[]={{"use-mtime",(void*)&PurgeUseMTime ,0,Fixed ,Boolean },
{"default" ,(void*)&DefaultPurgeAge,0,Fixed ,AgeDays },
{"max-size" ,(void*)&PurgeCacheSize ,0,Fixed ,FileSize},
{"" ,(void*)&PurgeAges ,1,ProtoAndHost,AgeDays },
{NULL ,NULL ,0,-1 ,-1 }};
/*+ The Purge section. +*/
static Section purge_section={"Purge",purge_entries};
/* All sections */
/*+ The list of sections (NULL terminated). +*/
static Section *sections[]={&startup_section,
&options_section,
&localhost_section,
&localnet_section,
&allowedconnect_section,
&dontcache_section,
&dontget_section,
&dontgetrecursive_section,
&censorheader_section,
&ftpoptions_section,
&mimetypes_section,
&proxy_section,
&mirror_section,
&purge_section,
NULL};
/* Local functions */
static int match_host_specification(char *proto,char *host,char *host_spec);
static int match_file_specification(char *path,char *file_spec);
static char *DefaultFTPPassWord(void);
static KeyPair **DefaultMirrorLinks(void);
static int ReadFile(FILE *config);
static int ReadSection(FILE *config,Section *section);
static int ParseEntry(Entry *entry,char *left,char *right);
static int ParseValue(char *text,ConfigType type,void *value,int rhs);
static int isanumber(const char *string);
static void SetDefaultValues(void);
static void SaveOldValues(void);
static void RestoreOldValues(void);
static void RemoveOldValues(void);
static void FreeKeyPairList(KeyPair **list,int andvalue);
/*+ Only the first time that the config file is read can the StartUp section be used. +*/
static int first_time=1;
/*+ The error message. +*/
static char *errmsg;
/*++++++++++++++++++++++++++++++++++++++
Read in the configuration file.
int fd The file descriptor to write the error message to.
++++++++++++++++++++++++++++++++++++++*/
int ReadConfigFile(int fd)
{
FILE* config;
int line_no;
if(first_time)
SetDefaultValues();
errmsg=NULL;
config=fopen(ConfigFile,"r");
if(!config)
{
write_formatted(fd,"Cannot open the configuration file '%s'\n",ConfigFile);
return(1);
}
SaveOldValues();
SetDefaultValues();
line_no=ReadFile(config);
if(errmsg)
RestoreOldValues();
else
RemoveOldValues();
fclose(config);
if(errmsg)
{
write_formatted(fd,"Syntax error at line %d in '%s'; %s\n",line_no,ConfigFile,errmsg);
free(errmsg);
}
if(first_time)
first_time=0;
if(errmsg)
return(1);
else
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Get the first specified name of the server host.
char *GetLocalHost Returns the first named localhost.
int port If true then return the port as well.
++++++++++++++++++++++++++++++++++++++*/
char *GetLocalHost(int port)
{
char *localhost,*ret;
if(LocalHost)
localhost=(*LocalHost)->name;
else
localhost="localhost";
ret=(char*)malloc(strlen(localhost)+8);
if(port)
sprintf(ret,"%s:%d",localhost,HTTP_Port);
else
strcpy(ret,localhost);
return(ret);
}
/*++++++++++++++++++++++++++++++++++++++
Check if the specified hostname is to be cached.
int IsLocalNetHost Return true if the host is on the local network.
char *host The name of the host (and port number) to be checked.
++++++++++++++++++++++++++++++++++++++*/
int IsLocalNetHost(char *host)
{
KeyPair **p;
char *colon=strchr(host,':');
int isip=isdigit(*host);
int isit=0;
if(colon)
*colon=0;
if(LocalHost)
for(p=LocalHost;(*p)->name;p++)
if(!strcmp((*p)->name,host))
{isit=1;break;}
if(LocalNet && !isit)
for(p=LocalNet;(*p)->name;p++)
if(strlen((*p)->name)<=strlen(host) &&
((!isip && !strcmp ((*p)->name,host+strlen(host)-strlen((*p)->name))) ||
( isip && !strncmp((*p)->name,host,strlen((*p)->name)))))
{isit=1;break;}
if(colon)
*colon=':';
return(isit);
}
/*++++++++++++++++++++++++++++++++++++++
Check if the specified hostname is allowed to connect.
int IsAllowedConnect Return true if it is allowed to connect.
char *host The name of the host (and port number) to be checked.
++++++++++++++++++++++++++++++++++++++*/
int IsAllowedConnect(char *host)
{
KeyPair **p;
int isip=isdigit(*host);
int isit=0;
if(LocalHost)
for(p=LocalHost;(*p)->name;p++)
if(!strcmp((*p)->name,host))
{isit=1;break;}
if(AllowedConnect && !isit)
for(p=AllowedConnect;(*p)->name;p++)
if(strlen((*p)->name)<=strlen(host) &&
((!isip && !strcmp ((*p)->name,host+strlen(host)-strlen((*p)->name))) ||
( isip && !strncmp((*p)->name,host,strlen((*p)->name)))))
{isit=1;break;}
return(isit);
}
/*++++++++++++++++++++++++++++++++++++++
Check if the specified URL is to be cached.
int IsNotCached Return true if it is not to be cached.
char *proto The protocol to be checked.
char *host The name of the host to be checked.
char *path The path on the host to be checked.
++++++++++++++++++++++++++++++++++++++*/
int IsNotCached(char *proto,char *host,char *path)
{
KeyPair **d;
if(DontCache)
for(d=DontCache;(*d)->name;d++)
if(match_host_specification(proto,host,(*d)->name))
if(match_file_specification(path,(*d)->value.string))
return(1);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Check if the specified URL is to be got.
int IsNotGot Return true if it is not to be got.
char *proto The protocol to be checked.
char *host The name of the host (and port number) to be checked.
char *path The path on the host to be checked.
++++++++++++++++++++++++++++++++++++++*/
int IsNotGot(char *proto,char *host,char *path)
{
KeyPair **d;
if(DontGet)
for(d=DontGet;(*d)->name;d++)
if(match_host_specification(proto,host,(*d)->name))
if(match_file_specification(path,(*d)->value.string))
return(1);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Check if the specified URL is to be got recursively.
int IsNotGotRecursive Return true if it is not to be got recursively.
char *proto The protocol to be checked.
char *host The name of the host (and port number) to be checked.
char *path The path on the host to be checked.
++++++++++++++++++++++++++++++++++++++*/
int IsNotGotRecursive(char *proto,char *host,char *path)
{
KeyPair **d;
if(DontGetRecursive)
for(d=DontGetRecursive;(*d)->name;d++)
if(match_host_specification(proto,host,(*d)->name))
if(match_file_specification(path,(*d)->value.string))
return(1);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Decide if the header line is to be sent to the server.
int IsCensoredheader Returns true if the header line is to be deleted.
char *line The line to check.
++++++++++++++++++++++++++++++++++++++*/
int IsCensoredHeader(char *line)
{
KeyPair **p;
int isit=0;
if(CensorHeader && !isit)
for(p=CensorHeader;(*p)->name;p++)
if(line[strlen((*p)->name)]==':' &&
!strncmp((*p)->name,line,strlen((*p)->name)))
{isit=1;break;}
return(isit);
}
/*++++++++++++++++++++++++++++++++++++++
Determine an email address to use as the FTP password.
char *DefaultFTPPassword Returns a best-guess password.
++++++++++++++++++++++++++++++++++++++*/
static char *DefaultFTPPassWord(void)
{
struct passwd *pwd;
char *username,*fqdn,*password;
pwd=getpwuid(getuid());
if(!pwd)
username="root";
else
username=pwd->pw_name;
fqdn=GetFQDN();
if(!fqdn)
fqdn="";
password=(char*)malloc(strlen(username)+strlen(fqdn)+4);
sprintf(password,"%s@%s",username,fqdn);
return(password);
}
/*++++++++++++++++++++++++++++++++++++++
Determine what username and password to use for the FTP server.
int WhatFTPUserPass Return 1 if there is a non-default one.
char *host The FTP server.
char **user Returns the username.
char **pass Returns the password.
++++++++++++++++++++++++++++++++++++++*/
int WhatFTPUserPass(char *host,char **user,char **pass)
{
int def=0;
KeyPair **h,**u,**p;
*user=FTPUserName;
*pass=FTPPassWord;
if(FTPAuthHost && FTPAuthUser && FTPAuthPass)
for(h=FTPAuthHost,u=FTPAuthUser,p=FTPAuthPass;(*h)->value.string && (*u)->value.string && (*p)->value.string;h++,u++,p++)
{
if(!strcmp(host,(*h)->value.string))
{
*user=(*u)->value.string;
*pass=(*p)->value.string;
def=1;
break;
}
}
return(def);
}
/*++++++++++++++++++++++++++++++++++++++
Decide what mime type to apply for a given file.
char *WhatMIMEType Returns the MIME Type.
char *path The path of the file.
++++++++++++++++++++++++++++++++++++++*/
char *WhatMIMEType(char *path)
{
char *mimetype=DefaultMIMEType;
int maxlen=0;
KeyPair **m;
if(MIMETypes)
for(m=MIMETypes;(*m)->name;m++)
if(strlen(path)>strlen((*m)->name) &&
strlen((*m)->name)>maxlen &&
!strcmp((*m)->name,path+strlen(path)-strlen((*m)->name)))
{mimetype=(*m)->value.string;maxlen=strlen((*m)->value.string);}
return(mimetype);
}
/*++++++++++++++++++++++++++++++++++++++
Determine the proxy to use for a specified host.
char *WhichProxy Return a pointer to the proxy.
char *proto The name of the protocol used.
char *host The name of the host to be contacted.
++++++++++++++++++++++++++++++++++++++*/
char *WhichProxy(char *proto,char *host)
{
KeyPair **p;
char *proxy=NULL;
int matchlen=0;
if(Proxies)
for(p=Proxies;(*p)->name;p++)
if(match_host_specification(proto,host,(*p)->name))
if(strlen((*p)->name)>matchlen)
{
matchlen=strlen((*p)->name);
proxy=(*p)->value.string;
}
return(proxy);
}
/*++++++++++++++++++++++++++++++++++++++
Determine what authentication password is required for the proxy.
char *WhatProxyAuth Returns the password:username combination.
char *proxy The name of the proxy.
++++++++++++++++++++++++++++++++++++++*/
char *WhatProxyAuth(char *proxy)
{
KeyPair **h,**u,**p;
char *user=NULL,*pass=NULL;
char *userpass=NULL;
if(ProxyAuthHost && ProxyAuthUser && ProxyAuthPass)
{
for(h=ProxyAuthHost,u=ProxyAuthUser,p=ProxyAuthPass;(*h)->value.string && (*u)->value.string && (*p)->value.string;h++,u++,p++)
{
if(!strcmp(proxy,(*h)->value.string))
{
user=(*u)->value.string;
pass=(*p)->value.string;
break;
}
}
if(user)
{
userpass=(char*)malloc(strlen(user)+strlen(pass)+2);
strcpy(userpass,user);
strcat(userpass,":");
strcat(userpass,pass);
}
}
return(userpass);
}
/*++++++++++++++++++++++++++++++++++++++
Check if a specified protocol and host is mirrored on another host.
int IsMirrored Returns non-zero if this is host is mirrored on another host.
char *proto The protocol to check.
char *host The hostname to check.
char **new_proto The protocol of the preferred mirror.
char **new_host The hostname of the preferred mirror.
++++++++++++++++++++++++++++++++++++++*/
int IsMirrored(char *proto,char *host,char **new_proto,char **new_host)
{
KeyPair **m;
*new_proto=*new_host=NULL;
if(Mirrors)
for(m=Mirrors;(*m)->name;m++)
{
char *slash1=strchr((*m)->name,'/'),*protopart1=NULL,*hostpart1=NULL;
if(slash1)
{*slash1=0;protopart1=(*m)->name;hostpart1=slash1+1;}
else
{protopart1=NULL;hostpart1=(*m)->name;}
if((protopart1==NULL || !strcmp(protopart1,proto)) &&
!strcmp(hostpart1,host))
{
char *slash2=strchr((*m)->value.string,'/'),*protopart2=NULL,*hostpart2=NULL;
if(slash2)
{*slash2=0;protopart2=(*m)->value.string;hostpart2=slash2+1;}
else
{protopart2=NULL;hostpart2=(*m)->value.string;}
if(protopart2)
{*new_proto=(char*)malloc(strlen(protopart2)+1);strcpy(*new_proto,protopart2);}
else
if(*protopart2)
{*new_proto=(char*)malloc(strlen(proto)+1);strcpy(*new_proto,proto);}
if(new_proto)
*new_host=(char*)malloc(strlen(hostpart2)+1);strcpy(*new_host,hostpart2);
if(slash2)
*slash2='/';
}
if(slash1)
*slash1='/';
}
return(*new_proto || *new_host);
}
/*++++++++++++++++++++++++++++++++++++++
Provide a list of all of the mirrored hosts for this protocol.
char **ListMirrors Returns a list with hostname and mirror hostname.
char *proto The protocol we are listing for.
++++++++++++++++++++++++++++++++++++++*/
char ***ListMirrors(char *proto)
{
char ***mirrors=NULL;
int nmirrors=0;
KeyPair **m;
if(Mirrors)
for(m=Mirrors;(*m)->name;m++)
{
char *slash1=strchr((*m)->name,'/'),*protopart1=NULL,*hostpart1=NULL;
if(slash1)
{*slash1=0;protopart1=(*m)->name;hostpart1=slash1+1;}
else
{protopart1=NULL;hostpart1=(*m)->name;}
if((protopart1==NULL || !strcmp(protopart1,proto)))
{
char *slash2=strchr((*m)->value.string,'/'),*protopart2=NULL,*hostpart2=NULL;
if(slash2)
{protopart2=(*m)->value.string;hostpart2=slash2+1;}
else
{protopart2=NULL;hostpart2=(*m)->value.string;}
if(!(nmirrors%16))
{
if(!mirrors)
mirrors=(char***)malloc(17*sizeof(char**));
else
mirrors=(char***)realloc(mirrors,(nmirrors+17)*sizeof(char**));
}
mirrors[nmirrors]=(char**)malloc(2*sizeof(char*));
mirrors[nmirrors][0]=(char*)malloc(strlen(hostpart1)+1);
strcpy(mirrors[nmirrors][0],hostpart1);
mirrors[nmirrors][1]=(char*)malloc((protopart2?strlen(protopart2):strlen(hostpart2))+4);
if(protopart2)
{
strcpy(mirrors[nmirrors][1],"../");
strcat(mirrors[nmirrors][1],protopart2);
}
else
strcpy(mirrors[nmirrors][1],hostpart2);
nmirrors++;
}
if(slash1)
*slash1='/';
}
if(mirrors)
mirrors[nmirrors]=NULL;
return(mirrors);
}
/*++++++++++++++++++++++++++++++++++++++
Search through the cache for the symbolic links that are also mirror entries.
KeyPair **DefaultMirrorLinks Return the list of links.
++++++++++++++++++++++++++++++++++++++*/
static KeyPair **DefaultMirrorLinks(void)
{
KeyPair **links=NULL;
int p;
for(p=0;p<NProtocols;p++)
{
struct stat buf;
if(!lstat(Protocols[p].name,&buf) && S_ISDIR(buf.st_mode))
{
DIR *dir2;
struct dirent* ent2;
if(chdir(Protocols[p].name))
{PrintMessage(Warning,"Cannot change to directory '%s' [%!s]; no mirrors.",Protocols[p].name);continue;}
/* Open the spool directory. */
dir2=opendir(".");
if(!dir2)
{PrintMessage(Warning,"Cannot open directory '%s' [%!s]; no mirrors.",Protocols[p].name);chdir("..");continue;}
ent2=readdir(dir2); /* skip . */
if(!ent2)
{PrintMessage(Warning,"Cannot read directory '%s' [%!s]; no mirrors.",Protocols[p].name);closedir(dir2);chdir("..");continue;}
ent2=readdir(dir2); /* skip .. */
/* Search for all of the symbolic links. */
while((ent2=readdir(dir2)))
{
struct stat buf2;
char *host=ent2->d_name;
if(!lstat(host,&buf2) && S_ISLNK(buf2.st_mode))
{
KeyPair **l;
char link[257],*link_proto=Protocols[p].name,*link_host=host;
int n,pp;
/* Follow the link. */
link[0]=0;
if((n=readlink(host,link,256))==-1)
{PrintMessage(Warning,"Cannot read link '%s/%s' [%!s].",Protocols[p].name,host);*link=0;break;}
link[n]=0;
if(!strncmp(link,"../",3))
{
link_proto=link+3;
link_host=link_proto+1;
while(link_host && *link_host!='/')
link_host++;
*link_host++=0;
}
else
link_host=link;
if(strchr(link_proto,'/') || strchr(link_host,'/'))
continue;
for(pp=0;pp<NProtocols;pp++)
if(!strcmp(Protocols[pp].name,link_proto))
break;
if(pp==NProtocols)
continue;
/* Add it to the list. */
if(!links)
{
links=(KeyPair**)malloc(8*sizeof(KeyPair*));
*links=&KeyPairEnd;
}
l=links;
for(n=0;(*l)->name;l++,n++);
if(n%8==7)
links=(KeyPair**)realloc(links,(n+9)*sizeof(KeyPair*));
for(l=links;(*l)->name;l++);
*l=(KeyPair*)malloc(sizeof(KeyPair));
(*l)->name=(char*)malloc(strlen(Protocols[p].name)+strlen(host)+2);
sprintf((*l)->name,"%s/%s",Protocols[p].name,host);
(*l)->value.string=(char*)malloc(strlen(link_proto)+strlen(link_host)+2);
sprintf((*l)->value.string,"%s/%s",link_proto,link_host);
l++;
*l=&KeyPairEnd;
}
}
closedir(dir2);
chdir("..");
}
}
return(links);
}
/*++++++++++++++++++++++++++++++++++++++
Determine the age to use when purging for a specified host.
int WhatPurgeAge Return the age in days.
char *proto The name of the protocol used.
char *host The name of the host to be purged.
++++++++++++++++++++++++++++++++++++++*/
int WhatPurgeAge(char *proto,char *host)
{
KeyPair **p;
int age=DefaultPurgeAge;
int matchlen=0;
if(PurgeAges)
for(p=PurgeAges;(*p)->name;p++)
if(match_host_specification(proto,host,(*p)->name))
if(strlen((*p)->name)>matchlen)
{
matchlen=strlen((*p)->name);
age=(*p)->value.integer;
}
return(age);
}
/*++++++++++++++++++++++++++++++++++++++
Check if a protocol and host match a HOST-SPECIFICATION in the config file.
int match_host_specification Return 1 if true else 0.
char *proto The protocol.
char *host The host.
char *host_spec The HOST-SPECIFICATION from the config file.
++++++++++++++++++++++++++++++++++++++*/
static int match_host_specification(char *proto,char *host,char *host_spec)
{
int match=0;
char *colon=strchr(host,':');
int isip=isdigit(*host);
char *slash=strchr(host_spec,'/'),*colon2,*protopart=NULL,*hostpart=NULL;
if(colon)
*colon=0;
if(slash)
{*slash=0;protopart=host_spec;hostpart=slash+1;}
else
{protopart=NULL;hostpart=host_spec;}
colon2=strchr(hostpart,':');
if(colon2)
*colon2=0;
if((protopart==NULL || !*protopart || !strcmp(protopart,proto)) &&
strlen(hostpart)<=strlen(host) &&
(!*hostpart ||
(!isip && !strcmp (hostpart,host+strlen(host)-strlen(hostpart))) ||
( isip && !strncmp(hostpart,host,strlen(hostpart)))) &&
((!colon && !colon2) || (colon2 && !*(colon2+1)) || (colon && colon2 && !strcmp(colon+1,colon2+1))))
match=1;
else
match=0;
if(slash)
*slash='/';
if(colon2)
*colon2=':';
if(colon)
*colon=':';
return(match);
}
/*++++++++++++++++++++++++++++++++++++++
Check if a path matches a FILE-SPECIFICATION in the config file.
int match_file_specification Return 1 if true else 0.
char *path The path.
char *file_spec The FILE-SPECIFICATION from the config file.
++++++++++++++++++++++++++++++++++++++*/
static int match_file_specification(char *path,char *file_spec)
{
int match=0;
if(*file_spec=='/' && !strncmp(file_spec,path,strlen(file_spec)))
match=1;
else if(*file_spec=='.' && strlen(path)>strlen(file_spec) && !strcmp(file_spec,path+strlen(path)-strlen(file_spec)))
match=1;
return(match);
}
/*++++++++++++++++++++++++++++++++++++++
Read the data from the file.
int ReadFile Returns the number of lines read.
FILE *config The file to read from.
++++++++++++++++++++++++++++++++++++++*/
static int ReadFile(FILE *config)
{
char *line=NULL;
int line_no=0;
while((line=fgets_realloc(line,config)))
{
int i;
char *l=line;
char *r=line+strlen(line)-1;
line_no++;
while(*l==' ' || *l=='\t' || *l=='\r' || *l=='\n')
l++;
if(!*l || *l=='#')
continue;
while(r>l && (*r==' ' || *r=='\t' || *r=='\r' || *r=='\n'))
*r--=0;
for(i=0;sections[i];i++)
if(!strcmp(sections[i]->name,l))
break;
if(!sections[i])
{errmsg=(char*)malloc(32+strlen(l));sprintf(errmsg,"Unrecognised section label '%s'.",l);break;}
else
line_no+=ReadSection(config,sections[i]);
if(errmsg)
break;
}
if(line)
free(line);
return(line_no);
}
/*++++++++++++++++++++++++++++++++++++++
Read all the data from a section.
int ReadSection Returns the number of lines read.
FILE *config The file to read from.
Section *section The section to read.
++++++++++++++++++++++++++++++++++++++*/
static int ReadSection(FILE *config,Section *section)
{
char *line=NULL;
int line_no=0;
int open_brace=0;
while((line=fgets_realloc(line,config)))
{
int i;
char *l=line;
char *r=line+strlen(line)-1;
line_no++;
while(*l==' ' || *l=='\t' || *l=='\r' || *l=='\n')
l++;
if(!*l || *l=='#')
continue;
while(r>l && (*r==' ' || *r=='\t' || *r=='\r' || *r=='\n'))
*r--=0;
if(*l=='{')
{
if(*++l)
{errmsg=(char*)malloc(32);strcpy(errmsg,"Open brace has trailing junk.");break;}
else
{
if(open_brace)
{errmsg=(char*)malloc(32);strcpy(errmsg,"Open brace repeated.");break;}
open_brace=1;
}
}
else if(*l=='}')
{
if(*++l)
{errmsg=(char*)malloc(32);strcpy(errmsg,"Close brace has trailing junk.");break;}
else
{
if(!open_brace)
{errmsg=(char*)malloc(32);strcpy(errmsg,"No open brace seen.");break;}
break;
}
}
else
{
if(!open_brace)
{errmsg=(char*)malloc(32);strcpy(errmsg,"Open brace missing.");break;}
if(!first_time && section==*sections)
continue;
for(i=0;section->entries[i].name;i++)
if(!*section->entries[i].name || !strncmp(section->entries[i].name,l,strlen(section->entries[i].name)))
{
char *ll,*rr;
if(*section->entries[i].name)
{
ll=l+strlen(section->entries[i].name);
if(*ll && *ll!='=' && *ll!=' ' && *ll!='\t' && *ll!='\r' && *ll!='\n')
continue;
}
else
{
ll=l;
while(*ll && *ll!='=' && *ll!=' ' && *ll!='\t' && *ll!='\r' && *ll!='\n')
ll++;
}
if(section->entries[i].right_type==None)
{
*ll=0;
r=NULL;
}
else
{
r=strchr(line,'=');
if(!r)
{errmsg=(char*)malloc(32);strcpy(errmsg,"No equal sign for entry.");break;}
*ll=0;
if(!*l)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Nothing to the left of the equal sign.");break;}
r++;
while(*r==' ' || *r=='\t' || *r=='\r' || *r=='\n')
r++;
rr=r;
while(*rr && *rr!=' ' && *rr!='\t' && *rr!='\r' && *rr!='\n')
rr++;
*rr=0;
}
break;
}
if(errmsg)
break;
if(!section->entries[i].name)
{errmsg=(char*)malloc(32+strlen(l));sprintf(errmsg,"Unrecognised entry label '%s'.",l);break;}
ParseEntry(§ion->entries[i],l,r);
if(errmsg)
break;
}
}
if(line)
free(line);
return(line_no);
}
/*++++++++++++++++++++++++++++++++++++++
Parse an entry from the file.
int ParseEntry Returns non-zero if there was an error.
Entry *entries The list of matching entry in the section.
char *left The string to the left of the equals sign.
char *right The string to the right of the equals sign.
++++++++++++++++++++++++++++++++++++++*/
static int ParseEntry(Entry *entry,char *left,char *right)
{
if(entry->list)
{
KeyPair **p,*new=(KeyPair*)malloc(sizeof(KeyPair));
int i;
if(entry->left_type==Fixed)
new->name=entry->name;
else
if(ParseValue(left,entry->left_type,(void*)&new->name,0))
{free(new);return(1);}
if(entry->right_type==None)
new->value.string=NULL;
else
if(ParseValue(right,entry->right_type,(void*)&new->value,1))
{free(new);return(1);}
if(!*(KeyPair***)entry->pointer)
{
*(KeyPair***)entry->pointer=(KeyPair**)malloc(8*sizeof(KeyPair*));
**(KeyPair***)entry->pointer=&KeyPairEnd;
}
p=*(KeyPair***)entry->pointer;
for(i=0;(*p)->name;p++,i++);
if(i%8==7)
*(KeyPair***)entry->pointer=(KeyPair**)realloc(*(void**)entry->pointer,(i+9)*sizeof(KeyPair*));
for(p=*(KeyPair***)entry->pointer;(*p)->name;p++);
*p=new;
p++;
*p=&KeyPairEnd;
}
else
if(ParseValue(right,entry->right_type,entry->pointer,1))
return(1);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Parse the text and put a value into the location.
int ParseValue Returns non-zero in case of error.
char *text The text string to parse.
ConfigType type The type we are looking for.
void *value The location to store the value.
int rhs Set to true if this is the right hand side.
++++++++++++++++++++++++++++++++++++++*/
static int ParseValue(char *text,ConfigType type,void *value,int rhs)
{
switch(type)
{
case Fixed:
case None:
break;
case Boolean:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a Boolean, got nothing.");}
else if(!strcasecmp(text,"yes") || !strcasecmp(text,"true"))
*(int*)value=1;
else if(!strcasecmp(text,"no") || !strcasecmp(text,"false"))
*(int*)value=0;
else
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a Boolean, got '%s'.",text);}
break;
case PortNumber:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a port number, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a port number, got '%s'.",text);}
else
{
*(int*)value=atoi(text);
if(*(int*)value<=0 || *(int*)value>65535)
{errmsg=(char*)malloc(32);sprintf(errmsg,"Invalid port number %d.",*(int*)value);}
}
break;
case AgeDays:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting an age in days, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting an age in days, got '%s'.",text);}
else
*(int*)value=atoi(text);
break;
case TimeSecs:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a time in seconds, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting an time in seconds, got '%s'.",text);}
else
*(int*)value=atoi(text);
break;
case FileSize:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a file size in MB, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a file size in MB, got '%s'.",text);}
else
{
*(int*)value=atoi(text);
if(*(int*)value<0)
{errmsg=(char*)malloc(48);sprintf(errmsg,"Invalid file size %d.",*(int*)value);}
}
break;
case CfgMaxServers:
if(!*text)
{errmsg=(char*)malloc(56);strcpy(errmsg,"Expecting a maximum server count, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a maximum server count, got '%s'.",text);}
else
{
*(int*)value=atoi(text);
if(*(int*)value<=0 || *(int*)value>MAX_SERVERS)
{errmsg=(char*)malloc(48);sprintf(errmsg,"Invalid maximum server count: %d.",*(int*)value);}
}
break;
case CfgMaxFetchServers:
if(!*text)
{errmsg=(char*)malloc(56);strcpy(errmsg,"Expecting a maximum fetch server count, got nothing.");}
else if(!isanumber(text))
{errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a maximum fetch server count, got '%s'.",text);}
else
{
*(int*)value=atoi(text);
if(*(int*)value<=0 || *(int*)value>MAX_FETCH_SERVERS)
{errmsg=(char*)malloc(48);sprintf(errmsg,"Invalid maximum fetch server count: %d.",*(int*)value);}
}
break;
case CfgLogLevel:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a log level, got nothing.");}
else if(strcasecmp(text,"debug")==0)
*(int*)value=Debug;
else if(strcasecmp(text,"info")==0)
*(int*)value=Inform;
else if(strcasecmp(text,"important")==0)
*(int*)value=Important;
else if(strcasecmp(text,"warning")==0)
*(int*)value=Warning;
else if(strcasecmp(text,"fatal")==0)
*(int*)value=Fatal;
else
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a log level, got '%s'.",text);}
break;
case UserId:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a username or uid, got nothing.");}
else
{
int uid;
struct passwd *userInfo=getpwnam(text);
if(userInfo)
uid=userInfo->pw_uid;
else
{
if(sscanf(text,"%d",&uid)!=1)
{errmsg=(char*)malloc(32+strlen(text));sprintf(errmsg,"Invalid user %s.",text);}
else if(!getpwuid(uid))
{errmsg=(char*)malloc(32);sprintf(errmsg,"Unknown user id %d.",uid);}
}
*(int*)value=uid;
}
break;
case GroupId:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a group name or gid, got nothing.");}
else
{
int gid;
struct group *groupInfo=getgrnam(text);
if(groupInfo)
gid=groupInfo->gr_gid;
else
{
if(sscanf(text,"%d",&gid)!=1)
{errmsg=(char*)malloc(32+strlen(text));sprintf(errmsg,"Invalid group %s.",text);}
else if(!getgrgid(gid))
{errmsg=(char*)malloc(32);sprintf(errmsg,"Unknown group id %d.",gid);}
}
*(int*)value=gid;
}
break;
case String:
if(!*text || !strcasecmp(text,"none"))
*(char**)value=NULL;
else
{
*(char**)value=(char*)malloc(strlen(text)+1);
strcpy(*(char**)value,text);
}
break;
case DirName:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a directory name, got nothing.");}
else if(*text!='/')
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting an absolute directory name, got '%s'.",text);}
else
{
*(char**)value=(char*)malloc(strlen(text)+1);
strcpy(*(char**)value,text);
}
break;
case PathOrFileExt:
if(!*text)
{errmsg=(char*)malloc(40);strcpy(errmsg,"Expecting a path name or file extension, got nothing.");}
else if(*text!='.' && *text!='/')
{errmsg=(char*)malloc(40+strlen(text));sprintf(errmsg,"Expecting a path name or file extension, got '%s'.",text);}
else
{
*(char**)value=(char*)malloc(strlen(text)+1);
strcpy(*(char**)value,text);
}
break;
case MIMEType:
if(!*text)
{errmsg=(char*)malloc(40);strcpy(errmsg,"Expecting a MIME Type, got nothing.");}
else
{
char *slash=strchr(text,'/');
if(!slash)
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a MIME Type/Subtype, got '%s'.",text);}
*(char**)value=(char*)malloc(strlen(text)+1);
strcpy(*(char**)value,text);
}
break;
case Host:
if(!*text)
{errmsg=(char*)malloc(40);strcpy(errmsg,"Expecting a hostname, got nothing.");}
else
{
char *colon=strchr(text,':'),*p;
if(colon)
{errmsg=(char*)malloc(56+strlen(text));sprintf(errmsg,"Expecting a hostname not a port number, got '%s'.",text);}
else
{
*(char**)value=(char*)malloc(strlen(text)+1);
for(p=text;*p;p++)
*p=tolower(*p);
strcpy(*(char**)value,text);
}
}
break;
case HostAndPortOrNone:
if(rhs && (!*text || !strcasecmp(text,"none")))
{
*(char**)value=NULL;
break;
}
/* fall through */
case HostAndPort:
if(!*text)
{errmsg=(char*)malloc(48);strcpy(errmsg,"Expecting a hostname (and port), got nothing.");}
else
{
char *colon=strchr(text,':'),*p;
if(*text==':')
{errmsg=(char*)malloc(48+strlen(text));sprintf(errmsg,"Expecting a hostname before the ':', got '%s'.",text);}
else
{
if(colon && (!isanumber(colon+1) || atoi(colon+1)<=0 || atoi(colon+1)>65535))
{errmsg=(char*)malloc(32+strlen(colon));sprintf(errmsg,"Invalid port number %s.",colon+1);}
else
{
*(char**)value=(char*)malloc(strlen(text)+1);
for(p=text;*p;p++)
*p=tolower(*p);
strcpy(*(char**)value,text);
}
}
}
break;
case ProtoAndHostOrDefault:
if(!strcmp("default",text))
{
*(char**)value=(char*)malloc(2);
strcpy(*(char**)value,"/");
break;
}
/* fall through */
case ProtoAndHost:
if(!*text)
{errmsg=(char*)malloc(64);strcpy(errmsg,"Expecting a protoocol and hostname, got nothing.");}
else
{
char *p;
*(char**)value=(char*)malloc(strlen(text)+1);
for(p=text;*p;p++)
*p=tolower(*p);
strcpy(*(char**)value,text);
}
break;
}
if(errmsg)
return(1);
else
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Decide if a string is an integer.
int isanumber Returns 1 if it is, 0 if not.
const char *string The string that may be an integer.
++++++++++++++++++++++++++++++++++++++*/
static int isanumber(const char *string)
{
int i=0;
if(string[0]=='-' || string[i]=='+')
i++;
for(;string[i];i++)
if(!isdigit(string[i]))
return(0);
return(1);
}
/*+ The old value of the level of error logging, see ErrorLevel in errors.h +*/
static int old_LogLevel;
/*+ The old value of the option to also fetch images. +*/
static int old_FetchImages;
/*+ The old value of the option to also fetch frames. +*/
static int old_FetchFrames;
/*+ The old value of the number of days to display in the index of the latest pages. +*/
static int old_IndexLatestDays;
/*+ The old value of the option for a tag that can be added to the bottom of the spooled pages with the date and a refresh button. +*/
static int old_AddInfoRefresh;
/*+ The old value of the maximum age of a cached page to use in preference while online. +*/
static int old_RequestChanged;
/*+ The old value of the option to allow or ignore the 'Pragma: no-cache' request. +*/
static int old_PragmaNoCache;
/*+ The old value of the option to not make requests while offline but to return an error. +*/
static int old_OfflineRequests;
/*+ The old value of the interval in days between monitoring the specified pages. +*/
static int old_MonitorInterval;
/*+ The old values of the list of localhost hostnames. +*/
static KeyPair **old_LocalHost;
/*+ The old value of the list of local network hostnames. +*/
static KeyPair **old_LocalNet;
/*+ The old values of the list of allowed hostnames. +*/
static KeyPair **old_AllowedConnect;
/*+ The old value of the list of servers and reasons not to cache. +*/
static KeyPair **old_DontCache;
/*+ The old value of the list of servers and reasons not to get. +*/
static KeyPair **old_DontGet;
/*+ The old value of the list of servers and reasons not to get. +*/
static KeyPair **old_DontGetRecursive;
/*+ The old_value of the list of censored headers. +*/
static KeyPair **old_CensorHeader;
/*+ The old value of the anon-ftp username. +*/
static char *old_FTPUserName;
/*+ The old value of the anon-ftp password. +*/
static char *old_FTPPassWord;
/*+ The old value of the information that is needed to allow non-anonymous access, +*/
static KeyPair **old_FTPAuthHost, /*+ hostname +*/
**old_FTPAuthUser, /*+ username +*/
**old_FTPAuthPass; /*+ password +*/
/*+ The old value of the default MIME type. +*/
static char *old_DefaultMIMEType;
/*+ The old value of the list of MIME types. +*/
static KeyPair **old_MIMETypes;
/*+ The old value of the list of hostnames and proxies. +*/
static KeyPair **old_Proxies;
/*+ The old value of the information that is needed to allow authorisation headers to be added, +*/
static KeyPair **old_ProxyAuthHost, /*+ hostname +*/
**old_ProxyAuthUser, /*+ username +*/
**old_ProxyAuthPass; /*+ password +*/
/*+ The old value of the list of protocols/hostnames and their mirrors. +*/
static KeyPair **old_Mirrors;
/*+ The old value of the flag that if true then use modification time instead of access time. +*/
static int old_PurgeUseMTime;
/*+ The old value of the default age for purging files. +*/
static int old_DefaultPurgeAge;
/*+ The old value of the maximum allowed size of the cache. +*/
static int old_PurgeCacheSize;
/*+ The old value of the list of hostnames and purge ages. +*/
static KeyPair **old_PurgeAges;
/*++++++++++++++++++++++++++++++++++++++
Set default values.
++++++++++++++++++++++++++++++++++++++*/
static void SetDefaultValues(void)
{
LogLevel=Important; /* see also variable declaration. */
FetchImages=0;
FetchFrames=0;
IndexLatestDays=7;
AddInfoRefresh=0;
RequestChanged=600;
PragmaNoCache=1;
OfflineRequests=1;
MonitorInterval=7;
LocalHost=NULL;
LocalNet=NULL;
AllowedConnect=NULL;
DontCache=NULL;
DontGet=NULL;
DontGetRecursive=NULL;
CensorHeader=NULL;
FTPUserName=(char*)malloc(16); strcpy(FTPUserName,"anonymous");
FTPPassWord=DefaultFTPPassWord();
FTPAuthHost=NULL;
FTPAuthUser=NULL;
FTPAuthPass=NULL;
DefaultMIMEType=(char*)malloc(32); strcpy(DefaultMIMEType,"text/plain");
MIMETypes=NULL;
Proxies=NULL;
ProxyAuthHost=NULL;
ProxyAuthUser=NULL;
ProxyAuthPass=NULL;
Mirrors=DefaultMirrorLinks();
PurgeUseMTime=0;
DefaultPurgeAge=DEF_PURGE_AGE;
PurgeCacheSize=0;
PurgeAges=NULL;
}
/*++++++++++++++++++++++++++++++++++++++
Save the old values in case the re-read of the file fails.
++++++++++++++++++++++++++++++++++++++*/
static void SaveOldValues(void)
{
old_LogLevel=LogLevel;
old_FetchImages=FetchImages;
old_FetchFrames=FetchFrames;
old_IndexLatestDays=IndexLatestDays;
old_AddInfoRefresh=AddInfoRefresh;
old_RequestChanged=RequestChanged;
old_PragmaNoCache=PragmaNoCache;
old_OfflineRequests=OfflineRequests;
old_MonitorInterval=MonitorInterval;
old_LocalHost=LocalHost;
old_LocalNet=LocalNet;
old_AllowedConnect=AllowedConnect;
old_DontCache=DontCache;
old_DontGet=DontGet;
old_DontGetRecursive=DontGetRecursive;
old_CensorHeader=CensorHeader;
old_FTPUserName=FTPUserName;
old_FTPPassWord=FTPPassWord;
old_FTPAuthHost=FTPAuthHost;
old_FTPAuthUser=FTPAuthUser;
old_FTPAuthPass=FTPAuthPass;
old_DefaultMIMEType=DefaultMIMEType;
old_MIMETypes=MIMETypes;
old_Proxies=Proxies;
old_ProxyAuthHost=ProxyAuthHost;
old_ProxyAuthUser=ProxyAuthUser;
old_ProxyAuthPass=ProxyAuthPass;
old_Mirrors=Mirrors;
old_PurgeUseMTime=PurgeUseMTime;
old_DefaultPurgeAge=DefaultPurgeAge;
old_PurgeCacheSize=PurgeCacheSize;
old_PurgeAges=PurgeAges;
}
/*++++++++++++++++++++++++++++++++++++++
Restore the old values if the re-read failed.
++++++++++++++++++++++++++++++++++++++*/
static void RestoreOldValues(void)
{
LogLevel=old_LogLevel;
FetchImages=old_FetchImages;
FetchFrames=old_FetchFrames;
IndexLatestDays=old_IndexLatestDays;
AddInfoRefresh=old_AddInfoRefresh;
RequestChanged=old_RequestChanged;
PragmaNoCache=old_PragmaNoCache;
OfflineRequests=old_OfflineRequests;
MonitorInterval=old_MonitorInterval;
if(LocalHost)
FreeKeyPairList(LocalHost,1);
LocalHost=old_LocalHost;
if(LocalNet)
FreeKeyPairList(LocalNet,1);
LocalNet=old_LocalNet;
if(AllowedConnect)
FreeKeyPairList(AllowedConnect,1);
AllowedConnect=old_AllowedConnect;
if(DontCache)
FreeKeyPairList(DontCache,3);
DontCache=old_DontCache;
if(DontGet)
FreeKeyPairList(DontGet,3);
DontGet=old_DontGet;
if(DontGetRecursive)
FreeKeyPairList(DontGetRecursive,3);
DontGetRecursive=old_DontGetRecursive;
if(CensorHeader)
FreeKeyPairList(CensorHeader,1);
CensorHeader=old_CensorHeader;
if(FTPUserName)
free(FTPUserName);
FTPUserName=old_FTPUserName;
if(FTPPassWord)
free(FTPPassWord);
FTPPassWord=old_FTPPassWord;
if(FTPAuthHost)
FreeKeyPairList(FTPAuthHost,2);
FTPAuthHost=old_FTPAuthHost;
if(FTPAuthUser)
FreeKeyPairList(FTPAuthUser,2);
FTPAuthUser=old_FTPAuthUser;
if(FTPAuthPass)
FreeKeyPairList(FTPAuthPass,2);
FTPAuthPass=old_FTPAuthPass;
if(DefaultMIMEType)
free(DefaultMIMEType);
DefaultMIMEType=old_DefaultMIMEType;
if(MIMETypes)
FreeKeyPairList(MIMETypes,3);
MIMETypes=old_MIMETypes;
if(Proxies)
FreeKeyPairList(Proxies,3);
Proxies=old_Proxies;
if(ProxyAuthHost)
FreeKeyPairList(ProxyAuthHost,2);
ProxyAuthHost=old_ProxyAuthHost;
if(ProxyAuthUser)
FreeKeyPairList(ProxyAuthUser,2);
ProxyAuthUser=old_ProxyAuthUser;
if(ProxyAuthPass)
FreeKeyPairList(ProxyAuthPass,2);
ProxyAuthPass=old_ProxyAuthPass;
if(Mirrors)
FreeKeyPairList(Mirrors,3);
Mirrors=old_Mirrors;
PurgeUseMTime=old_PurgeUseMTime;
DefaultPurgeAge=old_DefaultPurgeAge;
PurgeCacheSize=old_PurgeCacheSize;
if(PurgeAges)
FreeKeyPairList(PurgeAges,1);
PurgeAges=old_PurgeAges;
}
/*++++++++++++++++++++++++++++++++++++++
Remove the old values if the re-read succeeded.
++++++++++++++++++++++++++++++++++++++*/
static void RemoveOldValues(void)
{
if(old_LocalHost)
FreeKeyPairList(old_LocalHost,1);
if(old_LocalNet)
FreeKeyPairList(old_LocalNet,1);
if(old_AllowedConnect)
FreeKeyPairList(old_AllowedConnect,1);
if(old_DontCache)
FreeKeyPairList(old_DontCache,3);
if(old_DontGet)
FreeKeyPairList(old_DontGet,3);
if(old_DontGetRecursive)
FreeKeyPairList(old_DontGetRecursive,3);
if(old_CensorHeader)
FreeKeyPairList(old_CensorHeader,1);
if(old_FTPUserName)
free(old_FTPUserName);
if(old_FTPPassWord)
free(old_FTPPassWord);
if(old_FTPAuthHost)
FreeKeyPairList(old_FTPAuthHost,2);
if(old_FTPAuthUser)
FreeKeyPairList(old_FTPAuthUser,2);
if(old_FTPAuthPass)
FreeKeyPairList(old_FTPAuthPass,2);
if(old_DefaultMIMEType)
free(old_DefaultMIMEType);
if(old_MIMETypes)
FreeKeyPairList(old_MIMETypes,3);
if(old_Proxies)
FreeKeyPairList(old_Proxies,3);
if(old_ProxyAuthHost)
FreeKeyPairList(old_ProxyAuthHost,2);
if(old_ProxyAuthUser)
FreeKeyPairList(old_ProxyAuthUser,2);
if(old_ProxyAuthPass)
FreeKeyPairList(old_ProxyAuthPass,2);
if(old_Mirrors)
FreeKeyPairList(old_Mirrors,3);
if(old_PurgeAges)
FreeKeyPairList(old_PurgeAges,1);
}
/*++++++++++++++++++++++++++++++++++++++
Free a KeyPair list.
KeyPair **list The list to free.
int freewhat If 1 then free the name, if 2 then free the value, if 3 free both, if 0 free neither.
++++++++++++++++++++++++++++++++++++++*/
static void FreeKeyPairList(KeyPair **list,int freewhat)
{
KeyPair **p;
for(p=list;(*p)->name;p++)
{
if(freewhat&1)
free((*p)->name);
if(freewhat&2 && (*p)->value.string)
free((*p)->value.string);
free(*p);
}
free(list);
}
#else /* SIMPLE */
char *GetLocalHost(int port)
{
static char ret[16];
if(port)
sprintf(ret,"localhost:%d",HTTP_Port);
else
sprintf(ret,"localhost");
return(ret);
}
#endif /* SIMPLE */