/*- * 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 #include #include #include #include #include #ifdef BSD #include #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\n", VERSION_TAG, VERSION, VERSION_TAG); fprintf(config, "<%s>%lu\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, "\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, "\n", SHUTDOWN_TYPE_TAG); fprintf(config, "<%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\n", PROGRESS_WINDOW_ON_TOP_TAG, (specs->progress_window_always_on_top ? TRUE_VALUE : FALSE_VALUE), PROGRESS_WINDOW_ON_TOP_TAG); fclose(config); }