/* 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 #endif #define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #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//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 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); } }