dockapps/wmnotify/src/configfile.c
2014-10-05 19:18:49 +01:00

427 lines
13 KiB
C

/*
* configfile.c -- Parsing the configuration file
*
* Copyright (C) 2003 Hugo Villeneuve <hugo@hugovil.com>
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdbool.h>
#if STDC_HEADERS
# include <string.h>
#elif HAVE_STRINGS_H
# include <strings.h>
#endif
#include <pwd.h>
#include "common.h"
#include "wmnotify.h"
#include "configfile.h"
#define LINE_BUFFER_LEN 256
/* Name of configuration file in user's home directory. */
const static char default_config_filename[] = ".wmnotifyrc";
const static char delimiter_single_arg[] = " \n";
const static char delimiter_multiple_arg[] = "#\n";
static void
CreateDefaultConfigurationFile( char *file )
{
int status;
FILE *fp;
fp = fopen( file, "w" );
if( fp == NULL ) {
fprintf( stderr, "%s: Can't create file \"%s\"\n", PACKAGE, file );
exit( EXIT_FAILURE );
}
/* Changing permissions so that only the user can read/modify the file. */
status = chmod( file, S_IRUSR | S_IWUSR );
if( status < 0 ) {
fprintf( stderr, "%s: Can't set permission bits on file \"%s\"\n", PACKAGE, file );
exit( EXIT_FAILURE );
}
fprintf( fp, "# ~/.wmnotifyrc -- Default configuration file for wmnotify\n\n" );
fprintf( fp, "# Replace all 'xxxxxxxx' fields with your own settings.\n\n" );
fprintf( fp, "# Parameters preceded by a '#' character are optional.\n" );
fprintf( fp, "# You can set their values by removing the leading '#'.\n\n" );
fprintf( fp, "# Mail Protocol: POP3 or IMAP4.\n" );
fprintf( fp, "protocol POP3\n\n" );
fprintf( fp, "# Use SSL encrytion: 0=disable, 1=enable (optional, default is "
"disabled).\n" );
fprintf( fp, "use_ssl 0\n\n" );
fprintf( fp, "# Mail Server Name.\n" );
fprintf( fp, "server xxxxxxxx\n\n" );
fprintf( fp, "# Mail Server Port Number (optional, default is 110).\n" );
fprintf( fp, "port 110\n\n" );
fprintf( fp, "# Username.\n" );
fprintf( fp, "username xxxxxxxx\n\n" );
fprintf( fp, "# Password.\n" );
fprintf( fp, "password xxxxxxxx\n\n" );
fprintf( fp, "# IMAP folder name (optional, default is INBOX).\n" );
fprintf( fp, "# folder INBOX.some_folder\n\n" );
fprintf( fp, "# Mail Check Interval (in minutes, default is 5 minutes).\n" );
fprintf( fp, "#mailcheckdelay 5\n\n" );
fprintf( fp, "# Default mail client (optional).\n" );
fprintf( fp, "#mailclient sylpheed\n\n" );
fprintf( fp, "# Audio notification, 0=disable, 1=enable (optional, default is "
"disabled).\n" );
fprintf( fp, "enablebeep 0\n\n" );
fprintf( fp, "# Location of sound file for audio notification. If no sound file is\n" );
fprintf( fp, "# specified, the console beep will be used instead.\n" );
fprintf( fp, "audiofile /usr/local/share/sounds/halmsgs.wav\n\n" );
fprintf( fp, "# Volume (0 to 100%%).\n" );
fprintf( fp, "volume 100\n" );
fprintf( stderr, "%s: A default configuration file has been created in your "
"home directory: \"%s\"\n", PACKAGE, file );
fprintf( stderr, "You must edit it before running %s.\n", PACKAGE );
status = fclose( fp );
if( status != EXIT_SUCCESS ) {
fprintf( stderr, "%s: Error closing file \"%s\"\n", PACKAGE, file );
}
}
static void
ParseCommand( char *line, /*@out@*/ char *argv[] )
{
int argc = 0;
while( *line != '\0' ) { /* if not the end of line ....... */
while( *line == ' ' || *line == '\t' || *line == '\n' ) {
*line++ = '\0'; /* replace white spaces with 0 */
}
*argv++ = line; /* save the argument position */
while( *line != '\0' && *line != ' ' && *line != '\t' && *line != '\n' ) {
line++; /* skip the argument until ... */
}
argc++;
if( argc == ARGV_LIMIT ) {
fprintf( stderr, "%s: Too much arguments for external command\n",
PACKAGE );
exit( EXIT_FAILURE );
}
}
*argv = NULL; /* mark the end of argument list */
}
static char *
GetArguments( char *parameter, bool single_argument )
{
char *token;
if( single_argument ) {
token = strtok( NULL, delimiter_single_arg );
}
else {
/* We search for a string terminated by either a '#' character (the rest of
the line is then a comment, which is simply ignored ), or the end of line
character '\n'. */
token = strtok( NULL, delimiter_multiple_arg );
}
if( token == NULL ) {
fprintf( stderr, "%s: Missing argument for \"%s\" parameter in "
"configuration file.\n", PACKAGE, parameter );
exit( EXIT_FAILURE );
}
return token;
}
static int
GetNumber( char *token, char *parameter )
{
char temp[32]; /* Check size ??? */
if( sscanf( token, "%[0123456789]", temp ) == 0 ) {
fprintf( stderr, "%s: Invalid argument for \"%s\" parameter in "
"configuration file.\n", PACKAGE, parameter );
exit( EXIT_FAILURE );
}
return atoi( temp );
}
static void
ParseConfigurationFile( FILE *file )
{
char line[LINE_BUFFER_LEN];
char *token;
bool protocol_found = false;
bool server_found = false;
bool username_found = false;
bool password_found = false;
const char *err_string = NULL;
/* Default values for optional parameters. */
strcpy( wmnotify_infos.imap_folder, "INBOX"); /* Default IMAP folder. */
wmnotify_infos.port = 110;
wmnotify_infos.mail_check_interval = 60; /* 1 minute interval. */
wmnotify_infos.audible_notification = false; /* Disabled. */
wmnotify_infos.use_ssl = false; /* Disabled. */
wmnotify_infos.mail_client_argv[0] = NULL; /* No default command. */
wmnotify_infos.audiofile[0] = '\0'; /* No default audio file. */
wmnotify_infos.volume = 100; /* 100% volume. */
/* Reading one line of data from the configuration file. */
/* char *fgets(char *s, int size, FILE *stream);
Reading stops after an EOF or a newline. If a newline is read, it is
stored into the buffer. A '\0' is stored after the last character in
the buffer. */
while( fgets( line, LINE_BUFFER_LEN, file ) != NULL ) {
token = strtok( line, delimiter_single_arg );
if( ( token == NULL ) || ( token[0] == '#' ) ) {
continue; /* Next iteration of the while() loop (next line). */
}
if( STREQ( token, "protocol" ) ) {
token = GetArguments( "protocol", true );
if( STREQ( token, "POP3" ) == true ) {
wmnotify_infos.protocol = POP3_PROTOCOL;
}
else if( STREQ( token, "IMAP4" ) == true ) {
wmnotify_infos.protocol = IMAP4_PROTOCOL;
}
else {
fprintf( stderr, "%s: protocol must be POP3 or IMAP4.\n", PACKAGE );
exit( EXIT_FAILURE );
}
protocol_found = true;
}
else if( STREQ( token, "imap_folder" ) ) {
token = GetArguments( "imap_folder", true );
/* Should check size before using strcpy(), or use strncopy() instead. */
strcpy( wmnotify_infos.imap_folder, token );
}
else if( STREQ( token, "use_ssl" ) ){
int number;
token = GetArguments( "use_ssl", true );
number = GetNumber( token, "use_ssl" );
if( number == 0 ) {
wmnotify_infos.use_ssl = false;
}
else if( number == 1 ) {
#if HAVE_SSL
wmnotify_infos.use_ssl = true;
#else
fprintf( stderr, "%s error: You must compile %s with SSL support to\n" \
"set parameter 'use_ssl' to true in configuration file\n", PACKAGE, PACKAGE );
exit( EXIT_FAILURE );
#endif
}
else {
fprintf( stderr, "%s: Invalid value for parameter 'use_ssl' in\n" \
"configuration file (must be 0 or 1): %d\n", PACKAGE, number );
exit( EXIT_FAILURE );
}
}
else if( STREQ( token, "server" ) ) {
token = GetArguments( "server", true );
strncpy( wmnotify_infos.server_name, token, MAX_STR_LEN );
server_found = true;
}
else if( STREQ( token, "port" ) ) {
token = GetArguments( "port", true );
wmnotify_infos.port = (u_int16_t) GetNumber( token, "port" );
}
else if( STREQ( token, "username" ) ) {
token = GetArguments( "username", true );
strncpy( wmnotify_infos.username, token, MAX_STR_LEN );
username_found = true;
}
else if( STREQ( token, "password" ) ) {
token = GetArguments( "password", true );
strncpy( wmnotify_infos.password, token, MAX_STR_LEN );
password_found = true;
}
else if( STREQ( token, "mailcheckdelay" ) ) {
int delay; /* delay in minutes. */
token = GetArguments( "mailcheckdelay", true );
/* GetNumber() will exit if a negative number is entered. */
delay = GetNumber( token, "mailcheckdelay" );
if( delay == 0 ) {
fprintf( stderr, "%s: Mail check interval must be greater than '0'\n",
PACKAGE );
exit( EXIT_FAILURE );
}
wmnotify_infos.mail_check_interval = (unsigned int) delay * 60;
}
else if( STREQ( token, "mailclient" ) ) {
token = GetArguments( "mailclient", false ); /* Multiple arguments */
strcpy( wmnotify_infos.mail_client_command, token );
ParseCommand( wmnotify_infos.mail_client_command,
wmnotify_infos.mail_client_argv );
}
else if( STREQ( token, "enablebeep" ) ){
int number;
token = GetArguments( "enablebeep", true );
number = GetNumber( token, "enablebeep" );
if( number == 0 ) {
wmnotify_infos.audible_notification = false;
}
else if( number == 1 ) {
wmnotify_infos.audible_notification = true;
}
else {
fprintf( stderr, "%s: Invalid value for for parameter 'enablebeep' in\n" \
"configuration file (must be 0 or 1): %d\n", PACKAGE, number );
exit( EXIT_FAILURE );
}
}
else if( STREQ( token, "audiofile" ) ) {
token = GetArguments( "audiofile", true );
/* Should check size before using strcpy(), or use strncopy() instead. */
strcpy( wmnotify_infos.audiofile, token );
}
else if( STREQ( token, "volume" ) ) {
token = GetArguments( "volume", true );
wmnotify_infos.volume = GetNumber( token, "volume" );
}
else {
fprintf( stderr, "%s: invalid parameter in configuration file: %s\n", PACKAGE,
token );
exit( EXIT_FAILURE );
}
token = strtok( NULL, delimiter_single_arg );
if( ( token != NULL ) && ( token[0] != '#' ) ) {
fprintf( stderr, "%s: Garbage at end of line in configuration file: %s\n", PACKAGE,
token );
exit( EXIT_FAILURE );
}
}
if( protocol_found == false ) {
err_string = "protocol";
}
else if( server_found == false ) {
err_string = "server";
}
else if( username_found == false ) {
err_string = "username";
}
else if( password_found == false ) {
err_string = "password";
}
else {
return; /* success */
}
/* Failure. */
fprintf( stderr, "%s: Mandatory parameter \"%s\" missing from configuration "
"file.\n", PACKAGE, err_string );
exit( EXIT_FAILURE );
}
/*******************************************************************************
* Read and parse the configuration file in the user's home directory
******************************************************************************/
void
ConfigurationFileInit( void )
{
FILE *fp;
int status;
size_t len;
/* Check if an optional configuration file was specified on the command
line. */
if( wmnotify_infos.optional_config_file != NULL ) {
/* Trying to open the file. */
fp = fopen( wmnotify_infos.optional_config_file, "r" );
if( fp == NULL ) {
perror( PACKAGE );
ErrorLocation( __FILE__, __LINE__ );
exit( EXIT_FAILURE );
}
}
else {
/* Using the default configuration file. */
char *home_dir;
char *default_config_file;
home_dir = getenv("HOME");
if( home_dir == NULL ) {
/* We're trying to expand ~/, but HOME isn't set. */
struct passwd *pw = getpwuid( getuid() );
if( pw != NULL ) {
home_dir = pw->pw_dir;
}
else {
fprintf( stderr, "%s: Couldn't determine user's home directory path\n",
PACKAGE );
exit( EXIT_FAILURE );
}
}
/* We add 1 to the length for the terminating character '\0'. */
len = strlen( home_dir ) + strlen( "/" ) + strlen( default_config_filename )
+ 1;
default_config_file = xmalloc( len, __FILE__, __LINE__ );
sprintf( default_config_file, "%s/%s", home_dir, default_config_filename );
fp = fopen( default_config_file, "r" );
if( fp == NULL ) {
/* If we cannot open the default configuration file, it probably means
it is missing, so we create it. */
CreateDefaultConfigurationFile( default_config_file );
free( default_config_file );
exit( EXIT_FAILURE );
}
free( default_config_file );
}
ParseConfigurationFile( fp );
status = fclose( fp );
if( status != EXIT_SUCCESS ) {
fprintf( stderr, "%s: Error closing configuration file.\n", PACKAGE );
ErrorLocation( __FILE__, __LINE__ );
exit( EXIT_FAILURE );
}
}