/* PureAdmin
 * Copyright (C) 2003 Isak Savo
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Configuration backend.
 *
 * Copyright (C) 2003-2006 Isak Savo
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include "globals.h"
#include "helper.h"
#include "cfg.h"

/* Global configuration structure */
cfg_t cfg;

/* cfg_write_settings: Writes the configuration to disk (~/.pureadminrc).
 *
 * Returns: TRUE if everything was saved.
 *	    FALSE if something went wrong. (i.e. file couldn't be opened)
 */
gboolean cfg_write_settings (void)
{
	FILE *f;
	gchar filename[FILEPATH_MAX];
   
	g_snprintf (filename, FILEPATH_MAX, "%s/" RCFILE, g_get_home_dir ());
	if (!(f = fopen (filename, "w")))
	{
		perror ("Unable to open config-file for writing");
		return FALSE;
	}
    
	fputs ("# -*- sh -*-\n"
	       "# ~/.pureadminrc - Configuration file for pureadmin\n"
	       "# All variables are in the form VARNAME=VALUE where VALUE should be an UN-quoted string\n"
	       "# Comment lines BEGINS with a '#'. Otherwise the # is considered a part of the value/variablename\n\n", f);

	fputs ("# Default UID and GID to assign to new virtual users. These should point to an un-priveliged system account.\n", f);
	if (cfg.default_uid > 0)
		fprintf (f, "defaultuid=%d\n", cfg.default_uid);
	if (cfg.default_gid > 0)
		fprintf (f, "defaultgid=%d\n\n", cfg.default_gid);
  
	if (cfg.default_home)
	{
		fputs ("# Default home is the directory which will be assigned to new users by default.\n", f);
		fprintf (f, "defaulthome=%s\n\n", cfg.default_home);
	}
  
	if (cfg.cmd_purepw)
	{
		fputs ("# Name and optional full path to the pure-pw command. Path is only needed if it is not installed in a standard location (i.e. Not in $PATH)\n", f);
		fprintf (f, "cmd_purepw=%s\n\n", cfg.cmd_purepw);
	}
  
	fputs ("# Name and location of the password files where pureftpd reads user-information. The pdb-file is a binary database file,\n", f);
	if (cfg.pwfile)
		fprintf (f, "pwfile=%s\n", cfg.pwfile);
	if (cfg.pdbfile)
		fprintf (f, "pdbfile=%s\n\n", cfg.pdbfile);
  
	if (cfg.logfile)
	{
		fputs ("# Location of the logfile where pureftpd writes logging info\n", f);
		fprintf (f, "logfile=%s\n\n", cfg.logfile);
	}
	fputs ("# Logging method, 0 means syslog and 1 means custom logfile\n", f);
	fprintf (f, "logmethod=%d\n", cfg.logmethod);

	fputs ("# If this is enabled, then DNS name lookups will be performed resulting in IP-addresses being\n"
	       "# converted to human readable names\n"
	       "#  Setting this to 0 will make PureAdmin seem faster and more responsive.\n", f);
	fprintf (f, "resolve_hostnames=%d\n\n", cfg.resolve_hostnames);

	fputs ("# Indicates wether the first-time user-dialog messagebox have been seen by the user\n", f);
	fprintf (f, "seen_usrdlg_welcome=%d\n\n", cfg.seen_usrdlg_welcome);

	fputs ("# Indicates wether the FAM Error dialog should be shown or not\n", f);
	fprintf (f, "show_fam_errmsg=%d\n\n", cfg.show_fam_errmsg);
	
	fputs ("# Should we display an icon in the system tray upon events?\n", f);
	fprintf (f, "use_tray_icon=%d\n\n", cfg.use_tray_icon);

	fputs ("# Character set to use for usernames\n", f);
	fprintf (f, "username_encoding=%s\n", cfg.uname_encoding);
	fprintf (f, "use_system_encoding=%d\n\n", cfg.use_system_encoding);

	fputs ("# True if window size and position should be restored\n", f);
	fprintf (f, "save_window_geometry=%d\n\n", cfg.save_window_geometry);
	fputs ("# The window size and position in x:y pairs\n", f);
	fprintf (f, "window_size=%d:%d\n", cfg.win_size[0], cfg.win_size[1]);
	fprintf (f, "window_pos=%d:%d\n", cfg.win_pos[0], cfg.win_pos[1]);
	fprintf (f, "divider_pos=%d\n", cfg.div_pos);
	fprintf (f, "show_advinfo=%d\n", cfg.show_advinfo);

	fclose (f);
	return TRUE;
}

/* cfg_read_settings: Reads the settings from user configuration file (~/.pureadminrc)
 *
 * Returns: TRUE if all went well
 *	    FALSE if something went wrong. (i.e. file couldn't be opened)
 */
gboolean cfg_read_settings (void)
{
	FILE *f;
	gchar filename[FILEPATH_MAX];
	gchar buf[LINE_MAX], **arr;

   
	g_snprintf (filename, FILEPATH_MAX, "%s/" RCFILE, g_get_home_dir ());
	if (!(f = fopen (filename, "r")))
	{
		pur_log_wrn ("Unable to open config-file for reading: %s", strerror (errno));
		pur_log_nfo ("Using default configuration\n");
		return FALSE;
	}
  
	while (fgets (buf, LINE_MAX, f))
	{
		g_strstrip (buf);
		if (*buf == '#' || *buf == '\0')
			continue;
      
		arr = g_strsplit (buf, "=", 2);
		if (!arr || !arr[0] || !arr[1])
			continue;
		g_strstrip (arr[0]);
		g_strstrip (arr[1]);
		gchar *key = arr[0];
		gchar *val = arr[1];
      
		if (*val == '\0') /* Ignore values that is empty (i.e. "") */
			continue;
		if (strncmp (key, "defaultuid", 10) == 0)
			cfg.default_uid = atoi (val);
		else if (strncmp (key, "defaultgid", 10) == 0)
			cfg.default_gid = atoi (val);
		else if (strncmp (key, "defaulthome", 11) == 0)
		{
			if (g_path_is_absolute(val))
			{
				g_free (cfg.default_home);
				cfg.default_home = g_strdup (val);
			}
		}
		else if (strncmp (key, "cmd_purepw", 10) == 0)
		{
			if ((g_path_is_absolute(val) && g_file_test(val, G_FILE_TEST_EXISTS)) ||
				g_find_program_in_path(val))
			{
				g_free (cfg.cmd_purepw);
				cfg.cmd_purepw = g_strdup (val);
			}
		}
		else if (strncmp (key, "pwfile", 6) == 0)
		{
			if (g_path_is_absolute(val))
			{
				g_free (cfg.pwfile);
				cfg.pwfile = g_strdup (val);
			}
		}
		else if (strncmp (key, "pdbfile", 7) == 0)
		{
			if (g_path_is_absolute(val))
			{
				g_free (cfg.pdbfile);
				cfg.pdbfile = g_strdup (val);
			}
		}
		else if (strncmp (key, "logfile", 7) == 0)
		{
			if (g_path_is_absolute(val))
			{
				g_free (cfg.logfile);
				cfg.logfile = g_strdup (val);
			}
		}
		else if (strncmp (key, "logmethod", 9) == 0)
		{
			cfg.logmethod = atoi (val);
			if (cfg.logmethod < 0 || cfg.logmethod >= NUM_LOGMETHODS)
				cfg.logmethod = 0;
		}
		else if (strncmp (key, "window_size", 11) == 0)
			sscanf (val,"%d:%d",&(cfg.win_size[0]), &(cfg.win_size[1]));
		else if (strncmp (key, "window_pos", 10) == 0)
			sscanf (val,"%d:%d",&(cfg.win_pos[0]), &(cfg.win_pos[1]));
		else if (strncmp (key, "divider_pos", 11) == 0)
			cfg.div_pos = atoi (val);
		else if (strncmp (key, "save_window_geometry", 20) == 0)
			cfg.save_window_geometry = atoi (val);
		else if (strncmp (key, "show_advinfo", 12) == 0)
			cfg.show_advinfo = atoi (val);
		else if (strncmp (key, "resolve_hostnames", 17) == 0)
			cfg.resolve_hostnames = atoi (val);
		else if (strncmp (key, "seen_usrdlg_welcome", 19) == 0)
			cfg.seen_usrdlg_welcome = atoi (val);
		else if (strncmp (key, "show_fam_errmsg", 15) == 0)
			cfg.show_fam_errmsg = atoi (val);
		else if (strncmp (key, "use_tray_icon", 13) == 0)
			cfg.use_tray_icon = atoi (val);
		else if (strncmp (key, "username_encoding", 17) == 0)
		{
			g_free (cfg.uname_encoding);
			cfg.uname_encoding = g_strdup (val);
		}
		else if (strncmp (key, "use_system_encoding", 19) == 0)
			cfg.use_system_encoding = atoi (val);
      
		g_strfreev (arr);
	}
	fclose (f);
   
	return TRUE;
}

void cfg_terminate (void)
{
	g_free (cfg.default_home);
	g_free (cfg.cmd_purepw);
	g_free (cfg.cmd_ftpwho);
	g_free (cfg.cmd_startstop);
	g_free (cfg.cmd_useradd);
	g_free (cfg.cmd_groupadd);
	g_free (cfg.pwfile);
	g_free (cfg.pdbfile);
	g_free (cfg.logfile);
	g_free (cfg.uname_encoding);
}

gchar *cfg_find_default_home (void)
{
	if (g_file_test ("/home/ftpuser", G_FILE_TEST_IS_DIR))
		return g_strdup ("/home/ftpuser");
	else if (g_file_test ("/home/ftpusers", G_FILE_TEST_IS_DIR))
		return g_strdup ("/home/ftpusers");
	else if (g_file_test ("/home/ftp", G_FILE_TEST_IS_DIR))
		return g_strdup ("/home/ftp");

	return g_strdup ("/home/ftpusers");
}

gchar *cfg_find_pwfile (void)
{
	if (g_file_test (g_getenv ("PURE_PASSWDFILE"), G_FILE_TEST_IS_REGULAR))
		return g_strdup (g_getenv ("PURE_PASSWDFILE"));
	else if (g_file_test ("/etc/pureftpd.passwd", G_FILE_TEST_IS_REGULAR))
		return g_strdup ("/etc/pureftpd.passwd");
	else if (g_file_test ("/etc/pure-ftpd/pureftpd.passwd", G_FILE_TEST_IS_REGULAR))
		return g_strdup ("/etc/pure-ftpd/pureftpd.passwd");
		 
	return NULL;
}

gchar *cfg_find_pdbfile (void)
{
	if (g_file_test (g_getenv ("PURE_DBFILE"), G_FILE_TEST_IS_REGULAR))
		return g_strdup (g_getenv ("PURE_DBFILE"));
	else if (g_file_test ("/etc/pureftpd.pdb", G_FILE_TEST_IS_REGULAR))
		return g_strdup ("/etc/pureftpd.pdb");
	else if (g_file_test ("/etc/pure-ftpd/pureftpd.pdb", G_FILE_TEST_IS_REGULAR))
		return g_strdup ("/etc/pure-ftpd/pureftpd.pdb");

	/* No existing, default to the same as pwfile but with pdb-extension */
	if (cfg.pwfile) {
		gchar *s, *tmp ;

		s = g_strdup (cfg.pwfile);
		tmp = strrchr(s, '.');
		if (tmp)
			*tmp = '\0';
		tmp = g_strconcat (s, ".pdb", NULL);
		g_free (s);

		return tmp;
	}

	/* We've done all we can, return NULL */
	return NULL;
	
}

guint cfg_find_ftpuser_uid (void)
{
	gchar *s, **arr;
	guint rv = 0;
	
	arr = NULL;
	s = misc_get_line_beginning_with ("/etc/passwd", "ftpuser");
	if (!s)
		return 0;
	
	arr = g_strsplit (s, ":", 0);
	
	if (arr_count (arr) > 2)
		rv = atoi (arr[2]);
	
	g_strfreev (arr);
	g_free (s);

	return rv;
}

guint cfg_find_ftpgroup_gid (void)
{
	gchar *s, **arr;
	guint rv = 0;
	
	arr = NULL;
	s = misc_get_line_beginning_with ("/etc/group", "ftpgroup");
	
	if (!s)
		return 0;
	
	arr = g_strsplit (s, ":", 0);
	
	if (arr_count (arr) > 2)
		rv = atoi (arr[2]);
	
	g_strfreev (arr);
	g_free (s);

	return rv;
}

gchar *cfg_get_pftp_config_dir (void)
{
	gchar *dir = NULL;
	if (cfg.pwfile)
		dir = g_path_get_dirname (cfg.pwfile);
	else if (cfg.pdbfile)
		dir = g_path_get_dirname (cfg.pdbfile);
	else if (g_file_test("/etc/pure-ftpd", G_FILE_TEST_IS_DIR))
		dir = g_strdup ("/etc/pure-ftpd");
	else
		dir = g_strdup ("/etc");

	return dir;
}

/* Gets the default password file for pureftpd, regardless of whether it exists or not */
gchar *cfg_get_pftp_default_pwfile (void)
{
	if (g_getenv ("PURE_PASSWDFILE") && g_path_is_absolute(g_getenv("PURE_PASSWDFILE")))
		return g_strdup (g_getenv ("PURE_PASSWDFILE"));
	gchar *dir = cfg_get_pftp_config_dir();
	gchar *f = g_build_filename(dir, "pureftpd.passwd", NULL);
	g_free(dir);
	return f;
}

/* Gets the default dbfile for pureftpd, regardless of whether it exists or not */
gchar *cfg_get_pftp_default_pdbfile (void)
{
	if (g_getenv ("PURE_DBFILE") && g_path_is_absolute(g_getenv("PURE_DBFILE")))
		return g_strdup (g_getenv ("PURE_DBFILE"));
	gchar *dir = cfg_get_pftp_config_dir();
	gchar *f = g_build_filename(dir, "pureftpd.pdb", NULL);
	g_free(dir);
	return f;
}

/* cfg_fill_missing: Tries to find sane defaults for values that there are no
 *		     configured setting for. This is mostly done by probing the
 *		     file system for the relevant files.
 */
void cfg_fill_missing (void)
{
	if (!cfg.cmd_purepw)
		cfg.cmd_purepw = g_find_program_in_path ("pure-pw");
	if (!cfg.cmd_ftpwho)
		cfg.cmd_ftpwho = g_find_program_in_path ("pure-ftpwho");
	if (!cfg.cmd_startstop) {
		gchar prg_arr[][20] = { "rc.pure-ftpd", "rc.pureftpd", "pure-ftpd", "pure-ftpd-mysql"};

		for (gint i = 0; i < G_N_ELEMENTS(prg_arr); i++)
		{
			gchar *s = prg_arr[i];
			cfg.cmd_startstop = misc_find_prog_in_dirs
				(s, "/etc/rc.d /etc/rc.d/init.d /etc/init.d /usr/local/etc/rc.d");
			if (cfg.cmd_startstop)
				break;
		}
	}
	if (!cfg.pwfile)
	{
		gchar *base = cfg_get_pftp_config_dir();
		cfg.pwfile = g_build_filename (base, "pureftpd.passwd", NULL);
		g_free (base);
	}
	if (!cfg.pdbfile)
	{
		gchar *base = cfg_get_pftp_config_dir();
		cfg.pdbfile = g_build_filename (base, "pureftpd.pdb", NULL);
		g_free (base);
	}
	if (!cfg.default_home) 
		cfg.default_home = cfg_find_default_home ();
	if (!cfg.pwfile)
		cfg.pwfile = cfg_find_pwfile ();
	if (!cfg.pdbfile)
		cfg.pdbfile = cfg_find_pdbfile ();
	/* Find User ID of ftpuser */
	if (!cfg.default_uid)
		cfg.default_uid = cfg_find_ftpuser_uid ();
	
	/* Find Group ID of ftpgroup */
	if (!cfg.default_gid)
		cfg.default_gid = cfg_find_ftpgroup_gid ();

	if (!cfg.cmd_useradd)
		cfg.cmd_useradd = g_find_program_in_path ("useradd");
	if (!cfg.cmd_groupadd)
		cfg.cmd_groupadd = g_find_program_in_path ("groupadd");
}

/* cfg_set_defaults: Sets default values to the configuration struct.
 *		     This should only be called ONCE and before any
 *		     attempt to access or modify the cfg struct!
 *
 *		     Note: this function has no logic to make wise
 *		     decisions. See the cfg_fill_missing for that!
 */
void cfg_set_defaults (void)
{
	memset (&cfg, 0, sizeof (cfg_t));

	cfg.logfile = g_strdup ("/var/log/messages");
	cfg.use_system_encoding = TRUE;
	cfg.uname_encoding = g_strdup ("UTF-8");
	cfg.logmethod = LOG_SYSLOG;
	cfg.show_fam_errmsg = TRUE;
}


syntax highlighted by Code2HTML, v. 0.9.1