/* 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.
*/
/*
* Misc helperfunctions used throughout the program
*
* -- NOTE:
* crypt() was taken and slightly modified from pureftpd: Copyright to the author(s)
* --
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define _XOPEN_SOURCE
#include <gtk/gtk.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>
#include "helper.h"
#include "usr_manager.h"
#include "globals.h"
#include "cfg.h"
G_GNUC_PURE gint arr_count (gchar **arr)
{
gint i = 0;
g_return_val_if_fail (arr, 0);
while (arr[i] != NULL)
i++;
return i;
}
G_GNUC_PURE gboolean misc_str_is_only_whitespace (const gchar *s1)
{
guint i;
for (i = 0; i < strlen (s1); i++)
if (!isspace (s1[i]))
return FALSE;
return TRUE;
}
/* create_array: Creates a null terminated array of strings containing all arguments passed
* to this function. The argument list MUST be null-terminated.
*
* Arguments:
* argc_out: If non-null, the number of items in the array will be stored here.
* ...: A null-terminated list of strings to add to the array.
*
* Example: gchar **myarr = create_array (NULL, "item1", "item2", "item3", NULL);
*/
gchar **create_array (gint *argc_out, ...)
{
va_list ap;
GPtrArray *arr;
gchar *cur;
arr = g_ptr_array_sized_new (20);
va_start (ap, argc_out);
while ((cur = va_arg(ap, gchar*)))
{
g_ptr_array_add (arr, g_strdup (cur));
}
g_ptr_array_add (arr, NULL);
va_end (ap);
if (argc_out)
*argc_out = arr->len;
return (gchar **) g_ptr_array_free (arr, FALSE);
}
/* Spawns a command and return its output (only from stdout) as a newly allocated
* string. */
gchar *misc_spawn_command (const gchar *cmdline)
{
gchar *rv = NULL;
gboolean success = g_spawn_command_line_sync (cmdline, &rv, NULL, NULL, NULL);
if (!success)
return NULL;
return rv;
}
/* Gets the contents of /proc/<PID>/file as a newly allocated string. */
gchar *misc_get_process_info (const gchar *pid, const gchar *file)
{
gchar *path = g_build_filename ("/proc", pid, file, NULL);
gchar *rv = NULL;
g_file_get_contents (path, &rv, NULL, NULL);
return rv;
}
/* string_to_utf8: This function will do its best trying to convert the
* input string to UTF-8. It will convert as many bytes as
* possible, appending partial conversion with the
* string 'Invalid Unicode'
* If 'str' is null, the empty string ("") is returned!
*
* Returns: the (partially) converted string. This must always be free'd
*/
gchar *string_to_utf8 (const gchar *str)
{
gchar *retval, *tmp;
GError *err = NULL;
gsize bytes_read, bytes_written;
gboolean ends_with_newline = FALSE;
gint valid_utf8bytes = 0;
#define INVALID_UNICODE_STRING g_strconcat (_("Invalid Unicode"), (ends_with_newline ? "\n" : ""), NULL)
g_return_val_if_fail (str, g_strdup(""));
if (str[strlen (str) - 1] == '\n')
ends_with_newline = TRUE;
if (g_utf8_validate (str, -1, (const gchar**) &tmp))
return g_strdup (str);
valid_utf8bytes = gpointer_diff(tmp,str) - 1;
retval = g_locale_to_utf8 (str, -1, &bytes_read, &bytes_written, &err);
if (err)
{
g_free (retval);
g_error_free (err);
err = NULL;
if (bytes_read > 0)
valid_utf8bytes = bytes_read;
tmp = g_locale_to_utf8 (str, valid_utf8bytes, NULL, NULL, &err);
if (G_UNLIKELY(err)) {
/* This should not even happen */
pur_log_wrn ("Couldn't even convert %d first bytes to UTF-8: %s", valid_utf8bytes, err->message);
g_error_free (err);
return INVALID_UNICODE_STRING;
}
/* Failed character encoding conversion, return partial string */
retval = g_strdup_printf ("%s (%s)%c", tmp, _("Invalid Unicode"), ends_with_newline ? '\n' : '\0');
g_free (tmp);
}
return retval;
}
/* string_to vuser_locale: Converts a string into the locale used by the virtual users
*
*/
gchar *string_to_vuser_locale (const char *str)
{
gchar *retval;
const gchar *charset;
GError *err = NULL;
if (cfg.use_system_encoding)
g_get_charset (&charset);
else
charset = cfg.uname_encoding;
retval = g_convert (str, -1,
charset,
"UTF-8",
NULL, NULL, &err);
if (err)
{
pur_log_err ("Unable to username string from UTF-8 to %s: %s", charset, err->message);
g_error_free (err);
}
/* We return NULL here if conversion failed! */
return retval;
}
/* Converts a string encoding in virtual users locale to UTF-8 */
gchar *string_from_vuser_locale (const char *str)
{
gchar *retval;
const gchar *charset;
GError *err = NULL;
if (cfg.use_system_encoding)
g_get_charset (&charset);
else
charset = cfg.uname_encoding;
retval = g_convert (str, -1,
"UTF-8",
charset,
NULL, NULL, &err);
if (err)
{
pur_log_err ("Unable to username string from %s to UTF-8: %s", charset, err->message);
g_error_free (err);
}
/* We return NULL here if conversion failed! */
return retval;
}
/* Searches 'haystack' for a case insensitive match of 'needle'
* This function most likely fails on UTF-8 encoded strings */
gboolean misc_str_isearch (const gchar *haystack, const gchar *needle)
{
gchar *n, *h;
gboolean rv;
g_return_val_if_fail (haystack != NULL, FALSE);
g_return_val_if_fail (needle != NULL, FALSE);
n = g_ascii_strdown (needle, -1);
h = g_ascii_strdown (haystack, -1);
if (strstr (h, n))
rv = TRUE;
else
rv = FALSE;
g_free (n);
g_free (h);
return rv;
}
/* Elipsize a string at length 'len'.
* Example: pur_elipsize ("eat my shorts", 6) => "eat my..." */
gchar *pur_elipzise (const gchar *orig_s, guint len)
{
gchar *rv;
if (strlen (orig_s) < len + 3)
return g_strdup (orig_s);
rv = (gchar *) g_strndup (orig_s, len + 3 + 1);
rv[len] = rv[len + 1] = rv[len + 2] = '.';
rv[len + 3] = '\0';
return rv;
}
gchar *filename_without_extension (const gchar *filename)
{
if (filename == NULL || *filename == '\0')
return g_strdup (filename);
const gchar *ext = strrchr (filename, '.');
if (ext == NULL)
return g_strdup (filename);
return g_strndup (filename, gpointer_diff (ext, filename));
}
gboolean misc_stop_server (GError **err)
{
GError *i_err = NULL;
gchar *cmd, *out_stdout, *out_stderr;
gint ret, exitstatus;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
cmd = g_strdup_printf ("%s stop", cfg.cmd_startstop);
pur_log_nfo ("Stopping server: %s", cmd);
ret = g_spawn_command_line_sync (cmd, &out_stdout, &out_stderr,
&exitstatus, &i_err);
g_free (cmd);
g_free (out_stdout);
g_free (out_stderr);
if (!ret) {
exitstatus = WEXITSTATUS(exitstatus);
if (exitstatus != 0)
pur_log_wrn ("Stop server failed, child exited abnormally: %d", exitstatus);
g_propagate_error (err, i_err);
return FALSE;
}
return TRUE;
}
gboolean misc_start_server (GError **err)
{
GError *i_err = NULL;
gchar *cmd, *out_stdout, *out_stderr;
gint ret, exitstatus;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
cmd = g_strdup_printf ("%s start", cfg.cmd_startstop);
pur_log_nfo ("Starting server: %s", cmd);
ret = g_spawn_command_line_sync (cmd, &out_stdout, &out_stderr,
&exitstatus, &i_err);
g_free (cmd);
/* FIXME: Do something clever with the output */
g_free (out_stdout);
g_free (out_stderr);
if (!ret) {
exitstatus = WEXITSTATUS(exitstatus);
if (exitstatus != 0)
pur_log_wrn ("Start server failed, child exited abnormally: %d", exitstatus);
g_propagate_error (err, i_err);
return FALSE;
}
return TRUE;
}
gint misc_close_connection (guint pid)
{
gint ret;
/* FIXME: There is no safe way to sanity check this.. but this prevents us from
* at least killing init and the kernel services :-) */
if (pid < 100)
return 0;
#ifndef SIGTERM
pur_log_wrn ("SIGTERM is not defined, defining it to 15");
#define SIGTERM 15
#endif /* SIGTERM */
ret = kill (pid, SIGTERM);
if (ret == -1)
{
perror ("kill");
pur_log_wrn ("Could not close kill process %d\n", pid);
return 0;
}
return 1;
}
gchar *misc_find_file_in_dirs (const gchar *filename, const gchar *dirs)
{
gchar *tmp, **dirs_arr, **start;
start = dirs_arr = g_strsplit (dirs, " ", 0);
while (*dirs_arr)
{
tmp = g_build_filename (*dirs_arr, filename, NULL);
if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR))
return tmp;
g_free (tmp);
dirs_arr++;
}
g_strfreev (start);
return NULL;
}
gchar *misc_find_prog_in_dirs (const gchar *filename, const gchar *dirs)
{
gchar *tmp, **dirs_arr, **start;
start = dirs_arr = g_strsplit (dirs, " ", 0);
while (*dirs_arr)
{
tmp = g_build_filename (*dirs_arr, filename, NULL);
if (g_file_test (tmp, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE))
return tmp;
g_free (tmp);
dirs_arr++;
}
g_strfreev (start);
return NULL;
}
gchar *misc_get_line_beginning_with (const gchar *filename, const gchar *needle)
{
FILE *fl;
gchar buf[200], *rv;
gint len;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (needle != NULL, NULL);
if ((fl = fopen (filename, "r")) == NULL)
{
pur_log_dbg ("helper.c: Unable to open %s for reading", filename);
return NULL;
}
rv = NULL;
len = strlen (needle);
while (!feof (fl))
{
fgets (buf, 200, fl);
if (strncmp (buf, needle, len) == 0) {
rv = g_strdup (buf);
break;
}
}
fclose (fl);
return rv;
}
/* Tries to locate the users web browser by the following heuristics:
* 1) Check $BROWSER environment variable
* 2) Check gconf for GNOME default
* 3) Check if any of the hardcoded defaults exists: konqueror, firefox, mozilla
*
* FIXME: Should be enhanced to get KDEs default, but I don't know how. Also, browser
* source should be dependent on which DE the user is running => check for KDE/GNOME!
*/
gchar *misc_find_webbrowser (void)
{
gchar *browser = NULL;
gchar *output = NULL;
/* First choise: the $BROWSER environment variable */
if (g_getenv("BROWSER")) {
pur_log_dbg ("Web browser found by $BROWSER envvar: %s", g_getenv ("BROWSER"));
return g_strdup (g_getenv("BROWSER"));
}
/* Check gconf if available - this is used by GNOME */
if (g_find_program_in_path("gconftool-2") &&
g_spawn_command_line_sync ("gconftool-2 -g "
"/desktop/gnome/url-handlers/http/enabled "
"/desktop/gnome/url-handlers/http/command", &output, NULL, NULL, NULL))
{
gchar **values = g_strsplit(g_strchomp(output), "\n", 2);
if (arr_count(values) == 2) {
pur_log_dbg ("Web browser found by gconftool-2: [%s], [%s])", values[0], values[1]);
if (strcmp (values[0], "true") == 0)
browser = g_strdup (values[1]);
}
g_strfreev (values);
}
g_free (stdout);
/* Had I known how to check for default browser in KDE, it would
* be done here. But I don't know this, so lets try some hard coded ones instead */
if (!browser) {
if (g_find_program_in_path ("konqueror"))
browser = g_strdup ("konqueror");
else if (g_find_program_in_path ("firefox"))
browser = g_strdup ("firefox");
else if (g_find_program_in_path ("mozilla"))
browser = g_strdup ("mozilla");
pur_log_dbg ("Web browser found by hard-coded defaults: %s", browser);
}
if (!browser)
return NULL;
if (!strstr (browser, "%s")) {
char *tmp = g_strconcat (browser, " %s", NULL);
g_free (browser);
browser = tmp;
}
return browser;
}
gboolean misc_create_passwd_file (const gchar *filename)
{
FILE *f;
if ((f = fopen (filename, "w")) == NULL)
{
pur_log_wrn ("Could not create password file: %s", strerror (errno));
return FALSE;
}
fclose (f);
return TRUE;
}
/* Creates a directory and all its components. Also sets the owner and group of the directory */
/* If owner and/or group shouldn't be set, specify -1 as uid/gid. */
gboolean misc_create_directory (const gchar *directory, gint uid, gint gid, GError **err)
{
gchar **parts;
gboolean rv = TRUE;
g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
if (!g_path_is_absolute(directory))
{
g_set_error(err, PUREADMIN_MAIN_ERROR,
PA_MAIN_ERROR_FAILED,
"'%s' is not a valid directory", directory);
return FALSE;
}
parts = g_strsplit(directory, G_DIR_SEPARATOR_S, -1);
GString *s = g_string_new("");
for (gint i = 1; parts[i] != NULL; i++)
{
g_string_append_c(s, '/');
g_string_append(s, parts[i]);
if (g_file_test (s->str, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
continue;
if (mkdir(s->str, 0755) != 0)
{
g_set_error(err, PUREADMIN_MAIN_ERROR,
PA_MAIN_ERROR_FAILED,
"Couldn't create directory: %s", g_strerror(errno));
rv = FALSE;
break;
}
}
g_strfreev(parts);
g_string_free(s, TRUE);
if (rv == TRUE)
chown(directory, uid, gid);
return rv;
}
/* Crypt functions taken pure-pw distributed with pureftpd source package:
* Copyright (c) 2001, 2002, 2003 Frank Denis <j@pureftpd.org> with the
* help of all Pure-FTPd contributors.
*
* Modified slightly to use GLibs random numbers
*/
gchar *misc_crypt_passwd (const gchar * const pwd)
{
static const char crcars[64] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
register const char *crypted;
if ((crypted = (const char *) /* Blowfish */
crypt("test", "$2a$07$1234567890123456789012")) != NULL &&
strcmp(crypted, "$2a$07$123456789012345678901uKO4"
"/IReKqBzRzT6YaajGvw20UBdHW7m") == 0) {
char salt[] = "$2a$07$0000000000000000000000";
int c = 28;
do {
c--;
salt[c] = crcars[g_random_int() & 63];
} while (c > 7);
return (char *) crypt(pwd, salt);
} else if ((crypted = (const char *) /* MD5 */
crypt("test", "$1$12345678$")) != NULL &&
strcmp(crypted, "$1$12345678$oEitTZYQtRHfNGmsFvTBA/") == 0) {
char salt[] = "$1$00000000";
int c = 10;
do {
c--;
salt[c] = crcars[g_random_int() & 63];
} while (c > 3);
return (char *) crypt(pwd, salt);
} else if ((crypted = (const char *) /* Extended DES */
crypt("test", "_.../1234")) != NULL &&
strcmp(crypted, "_.../1234PAPUVmqGzpU") == 0) {
char salt[] = "_.../0000";
int c = 8;
do {
c--;
salt[c] = crcars[g_random_int() & 63];
} while (c > 5);
return (char *) crypt(pwd, salt);
}
/* Simple DES */
{
char salt[] = "00";
salt[0] = crcars[g_random_int() & 63];
salt[1] = crcars[g_random_int() & 63];
return (char *) crypt(pwd, salt);
}
}
syntax highlighted by Code2HTML, v. 0.9.1