/*-
* Copyright (C) 2005-2007 Nick Withers. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "downtime.h"
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/stat.h>
#ifdef BSD
#include <sysexits.h>
#endif
#define VERSION_TAG "version"
#define TIMEOUT_TAG "timeout"
#define TIMEOUT_UNIT_TAG "timeout_unit"
#define SHUTDOWN_TYPE_TAG "shutdown_type"
#define MAIN_WINDOW_ON_TOP_TAG "main_on_top"
#define PROGRESS_WINDOW_ON_TOP_TAG "progress_on_top"
#define MINUTES_VALUE "minutes"
#define HOURS_VALUE "hours"
#define DAYS_VALUE "days"
#define POWEROFF_VALUE "power-off"
#define RESTART_VALUE "restart"
#define HALT_VALUE "halt"
#define SINGLE_USER_VALUE "single user"
#define TRUE_VALUE "TRUE"
#define FALSE_VALUE "FALSE"
typedef struct
{
char *value_string;
int value;
} value_mapping;
#define TIMEOUT_UNIT_PARAM_MAPPINGS { \
{"minutes", MINUTES}, \
{"hours", HOURS}, \
{"days", DAYS}}
#define SHUTDOWN_TYPE_PARAM_MAPPINGS { \
{"power-off", POWEROFF}, \
{"restart", RESTART}, \
{"halt", HALT}, \
{"single user", SINGLE_USER}}
#define TRUTH_PARAM_MAPPINGS { \
{"TRUE", TRUE}, \
{"FALSE", FALSE}}
static char * get_param(char *line);
static char * get_value(char *line);
/* Returns NULL on failure, with errno == 0 if there was no passwd entry for the user */
char *
get_config_path(void)
{
char *user_home, *config_path;
struct passwd *pw;
errno = 0; /* Have to set errno = 0 in order to be able to distinguish between there being no passwd entry for the user and there being an error */
if ((pw = getpwuid(geteuid())) == NULL)
return (NULL);
if ((user_home = cat_strings(pw->pw_dir, DIR_SEPARATOR_STRING)) == NULL || (config_path = cat_strings(user_home, CONFIG_FILE_NAME)) == NULL) /* Note that if pw->pw_dir == NULL, the user's home directory will be DIR_SEPARATOR */
{
free(user_home); /* If we're here, either user_home == NULL, in which case this free() is a no-op, or it was allocated (and this free() is necessary) but config_path == NULL (and doesn't need to be free()d) */
return (NULL);
}
free(user_home);
return (config_path);
}
static char *
get_param(char *line)
{
char *temp, *param;
if ((line = index(line, '<')) != NULL) /* Find the first occurrence of '<' in line, then truncate the '<' and everything before it */
line++;
else
return (NULL);
if ((temp = (char *) malloc(sizeof (char) * (strlen(line) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in get_param(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(temp, line);
if ((param = index(temp, '>')) != NULL) /* Find the first occurrence of '>' in param, then truncate the '>' and everything after it from param */
*param = '\0';
if ((param = (char *) malloc(sizeof (char) * (strlen(temp) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in get_param(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(param, temp);
free(temp);
return (param);
}
static char *
get_value(char *line)
{
char *temp;
char *value;
if ((line = index(line, '>')) != NULL)
line++;
else
return (NULL);
if ((temp = (char *) malloc(sizeof (char) * (strlen(line) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in get_value(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(temp, line);
if ((value = index(temp, '<')) != NULL)
*value = '\0';
if ((value = (char *) malloc(sizeof (char) * (strlen(line) + 1))) == NULL)
show_error_dialog(EX_OSERR, "malloc() returned NULL in get_value(). Error: \"%s\" (%d)", strerror(errno), errno);
strcpy(value, temp);
free(temp);
return (value);
}
/* Loads settings from the file at path config_path into the structure pointed to by specs */
char
load_user_specs(user_specs_t *specs, char *config_path)
{
FILE *config;
user_specs_t new_specs;
char *line, *param, *value, c, *endptr;
size_t line_number;
char found;
new_specs = *specs; /* We're going to update new_specs, then set *specs = new_specs iff we're able to read the configuration file properly */
if ((config = fopen(config_path, "r")) == NULL)
return (FALSE); /* fopen() will have set errno as appropriate */
new_specs.config_path = config_path;
line = NULL;
line_number = 1; /* We start reading from line 1 */
while ((c = getc(config)) != EOF)
{
found = FALSE;
if (c != '\n') /* If we're not at the end of a line... */
{
if (!isprint(c)) /* Found a non-printable character in the file */
{
show_stderr("Found a non-printable character in the configuration file \"%s\"", config_path);
fclose(config);
errno = EFTYPE; /* "Inappropriate file type or format" */
return (FALSE);
}
if (!cat_strchar(&line, c))
show_error_dialog(EX_OSERR, "Couldn't store configuration file input. Error: \"%s\" (%d)", strerror(errno), errno);
}
else if (((param = get_param(line)) != NULL) && ((value = get_value(line)) != NULL))
{
if (strcmp(param, VERSION_TAG) == 0) /* Do nothing... The version tag is there for possible future use */
found = TRUE;
else if (strcmp(param, TIMEOUT_TAG) == 0)
{
new_specs.timeout = (unsigned long) strtol(value, &endptr, 10);
if (!(*value != '\0' && *endptr == '\0'))
show_stderr("An unusable parameter - \"%s\" - was found for the option \"%s\" on line %lu of the saved configuration file \"%s\". This might mean that this instance of DownTime is out of date, the file itself is out of date, or that the file is corrupt", value, param, (unsigned long) line_number, config_path);
found = TRUE;
}
else if (strcmp(param, TIMEOUT_UNIT_TAG) == 0)
{
value_mapping timeout_mappings[] = TIMEOUT_UNIT_PARAM_MAPPINGS;
int i;
for (i = 0; i < (sizeof (timeout_mappings) / sizeof (*timeout_mappings)); i++)
{
if (strcasecmp(value, (*(timeout_mappings + i)).value_string) == 0)
{
new_specs.timeout_unit = (*(timeout_mappings + i)).value;
found = TRUE;
break;
}
}
}
else if (strcmp(param, SHUTDOWN_TYPE_TAG) == 0)
{
value_mapping shutdown_type_mappings[] = SHUTDOWN_TYPE_PARAM_MAPPINGS;
int i;
for (i = 0; i < (sizeof (shutdown_type_mappings) / sizeof (*shutdown_type_mappings)); i++)
{
if (strcasecmp(value, (*(shutdown_type_mappings + i)).value_string) == 0)
{
new_specs.shutdown_type = (*(shutdown_type_mappings + i)).value;
found = TRUE;
break;
}
}
}
else if (strcmp(param, MAIN_WINDOW_ON_TOP_TAG) == 0)
{
value_mapping truth_mappings[] = TRUTH_PARAM_MAPPINGS;
int i;
for (i = 0; i < (sizeof (truth_mappings) / sizeof (*truth_mappings)); i++)
{
if (strcasecmp(value, (*(truth_mappings + i)).value_string) == 0)
{
new_specs.main_window_always_on_top = (*(truth_mappings + i)).value;
found = TRUE;
break;
}
}
}
else if (strcmp(param, PROGRESS_WINDOW_ON_TOP_TAG) == 0)
{
value_mapping truth_mappings[] = TRUTH_PARAM_MAPPINGS;
int i;
for (i = 0; i < (sizeof (truth_mappings) / sizeof (*truth_mappings)); i++)
{
if (strcasecmp(value, (*(truth_mappings + i)).value_string) == 0)
{
new_specs.progress_window_always_on_top = (*(truth_mappings + i)).value;
found = TRUE;
break;
}
}
}
else
{
show_stderr("An unrecognised option - \"%s\" - was found in the saved configuration file \"%s\". This might mean that this instance of DownTime is out of date, the file itself is out of date, or that the file is corrupt", param, config_path);
found = TRUE;
}
if (!found)
show_stderr("An unrecognised parameter - \"%s\" - was found for the option \"%s\" on line %lu of the saved configuration file \"%s\". This might mean that this instance of DownTime is out of date, the file itself is out of date, or that the file is corrupt", value, param, (unsigned long) line_number, config_path);
line_number++;
free(param);
free(value);
free(line);
line = NULL;
}
else
{
line_number++;
free(param); /* If the above else if conditional isn't executed, we can be sure that either param == NULL, or param points to a valid location that needs to be free()d and value == NULL. In the first case, this free() will be a no-op, in the latter it will be necessary and a free() of value would be a no-op (so we don't do it) */
free(line);
line = NULL;
}
}
fclose(config);
*specs = new_specs;
return (TRUE);
}
void
save_user_specs(user_specs_t *specs, char *config_path)
{
FILE *config;
if ((config = fopen(config_path, "w")) == NULL)
{
show_warning_dialog("Unable to open \"%s\" for writing. Error \"%s\" (%d). Settings will not be saved", config_path, strerror(errno), errno);
return;
}
fprintf(config, "<%s>%s</%s>\n", VERSION_TAG, VERSION, VERSION_TAG);
fprintf(config, "<%s>%lu</%s>\n", TIMEOUT_TAG, specs->timeout, TIMEOUT_TAG);
fprintf(config, "<%s>", TIMEOUT_UNIT_TAG);
switch (specs->timeout_unit)
{
case MINUTES:
fprintf(config, "%s", MINUTES_VALUE);
break;
case HOURS:
fprintf(config, "%s", HOURS_VALUE);
break;
case DAYS:
fprintf(config, "%s", DAYS_VALUE);
break;
}
fprintf(config, "</%s>\n", TIMEOUT_UNIT_TAG);
fprintf(config, "<%s>", SHUTDOWN_TYPE_TAG);
switch (specs->shutdown_type)
{
case POWEROFF:
fprintf(config, "%s", POWEROFF_VALUE);
break;
case RESTART:
fprintf(config, "%s", RESTART_VALUE);
break;
case HALT:
fprintf(config, "%s", HALT_VALUE);
break;
case SINGLE_USER:
fprintf(config, "%s", SINGLE_USER_VALUE);
break;
}
fprintf(config, "</%s>\n", SHUTDOWN_TYPE_TAG);
fprintf(config, "<%s>%s</%s>\n", MAIN_WINDOW_ON_TOP_TAG, (specs->main_window_always_on_top ? TRUE_VALUE : FALSE_VALUE), MAIN_WINDOW_ON_TOP_TAG);
fprintf(config, "<%s>%s</%s>\n", PROGRESS_WINDOW_ON_TOP_TAG, (specs->progress_window_always_on_top ? TRUE_VALUE : FALSE_VALUE), PROGRESS_WINDOW_ON_TOP_TAG);
fclose(config);
}
syntax highlighted by Code2HTML, v. 0.9.1