about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/command/cmd_funcs.c9
-rw-r--r--src/common.c91
-rw-r--r--src/common.h2
-rw-r--r--src/config/account.c50
-rw-r--r--src/xmpp/avatar.c5
5 files changed, 117 insertions, 40 deletions
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index 55082765..59702fe3 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -4900,7 +4900,7 @@ cmd_sendfile(ProfWin *window, const char *const command, gchar **args)
 			free(filename);
 			return TRUE;
         }
-        
+
     if (access(filename, R_OK) != 0) {
         cons_show_error("Uploading '%s' failed: File not found!", filename);
         free(filename);
@@ -8921,9 +8921,10 @@ cmd_urlopen(ProfWin *window, const char *const command, gchar **args)
             return TRUE;
         }
 
-        gchar* cmd = prefs_get_string(PREF_URL_OPEN_CMD);
-        call_external(cmd, args[0]);
-        g_free(cmd);
+        gchar *argv[] = {prefs_get_string(PREF_URL_OPEN_CMD), args[0], NULL};
+        if (!call_external(argv, NULL, NULL)) {
+          cons_show_error("Unable to open url: check the logs for more information.");
+        }
     } else {
         cons_show("urlopen not supported in this window");
     }
diff --git a/src/common.c b/src/common.c
index 821acd3e..1de5f4ba 100644
--- a/src/common.c
+++ b/src/common.c
@@ -484,15 +484,90 @@ get_mentions(gboolean whole_word, gboolean case_sensitive, const char *const mes
     return mentions;
 }
 
-void
-call_external(const char *const exe, const char *const param)
+/*
+ * Take an NULL-terminated array used as the tokens of a command, and optionally
+ * pointers to the string arrays that will store each lines of the call standard
+ * output and standard error.
+ *
+ * argv - NULL-terminated string array containing the tokens of the command
+ *        line to spawn
+ * output_ptr - a pointer to the string array where to store spawned command
+ *              standard output
+ *              set to NULL to ignore the command standard output
+ * error_ptr - a pointer to the string array where to store spawned command
+ *             standard error
+ *             set to NULL to ignore the command standard error
+ *
+ * Returns:
+ * - TRUE if the command has been successfully spawned and exited normally
+ * - FALSE otherwise
+ */
+gboolean
+call_external(gchar **argv, gchar ***const output_ptr, gchar ***const error_ptr)
 {
-    GString *cmd = g_string_new("");
+    gchar *stdout_str = NULL;
+    gchar **stdout_str_ptr = &stdout_str;
+    gchar *stderr_str = NULL;
+    gchar **stderr_str_ptr = &stderr_str;
+    GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
+    gint status;
+    GError *error = NULL;
+    gchar *cmd = NULL;
+
+    cmd = g_strjoinv(" ", argv);
+    log_debug("Calling external: %s", cmd);
+
+    if (!output_ptr) {
+        stdout_str_ptr = NULL;
+        flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
+    }
+
+    if (!error_ptr) {
+        stderr_str_ptr = NULL;
+        flags |= G_SPAWN_STDERR_TO_DEV_NULL;
+    }
+
+    if (!g_spawn_sync (NULL, argv, NULL, flags, NULL, NULL, stdout_str_ptr, stderr_str_ptr, &status, &error)) {
+        log_error("Spawning '%s' failed: %s.", cmd, error->message);
+        g_error_free(error);
+        error = NULL;
+        return FALSE;
+    }
 
-    g_string_append_printf(cmd, "%s %s > /dev/null 2>&1", exe, param);
-    log_debug("Calling external: %s", cmd->str);
-    FILE *stream = popen(cmd->str, "r");
+    if (!g_spawn_check_exit_status(status, &error)) {
+        log_error("Calling '%s' failed: %s.", cmd, error->message);
+        g_error_free(error);
+        error = NULL;
+        g_free(cmd);
+        cmd = NULL;
+        g_free(stdout_str);
+        stdout_str = NULL;
+        stdout_str_ptr = NULL;
+        if (stderr_str && strlen(stderr_str)) {
+            log_error("Called command returned the following on stderr: %s.", stderr_str);
+        }
+        g_free(stderr_str);
+        stderr_str = NULL;
+        stderr_str_ptr = NULL;
+        return FALSE;
+    }
 
-    pclose(stream);
-    g_string_free(cmd, TRUE);
+    g_free(cmd);
+    cmd = NULL;
+
+    if (output_ptr) {
+        *output_ptr = g_strsplit(stdout_str, "\n", 0);
+        g_free(stdout_str);
+        stdout_str = NULL;
+        stdout_str_ptr = NULL;
+    }
+
+    if (error_ptr) {
+        *error_ptr = g_strsplit(stderr_str, "\n", 0);
+        g_free(stderr_str);
+        stderr_str = NULL;
+        stderr_str_ptr = NULL;
+    }
+
+    return TRUE;
 }
diff --git a/src/common.h b/src/common.h
index 108536ed..93e4a609 100644
--- a/src/common.h
+++ b/src/common.h
@@ -106,6 +106,6 @@ void get_file_paths_recursive(const char *directory, GSList **contents);
 
 char* get_random_string(int length);
 
-void call_external(const char *const exe, const char *const param);
+gboolean call_external(gchar **argv, gchar ***const output_ptr, gchar ***const error_ptr);
 
 #endif
diff --git a/src/config/account.c b/src/config/account.c
index 96397954..daa2fc77 100644
--- a/src/config/account.c
+++ b/src/config/account.c
@@ -194,32 +194,30 @@ account_eval_password(ProfAccount *account)
     assert(account != NULL);
     assert(account->eval_password != NULL);
 
-    // Evaluate as shell command to retrieve password
-    GString *cmd = g_string_new("");
-    g_string_append_printf(cmd, "%s 2>/dev/null", account->eval_password);
-
-    FILE *stream = popen(cmd->str, "r");
-    g_string_free(cmd, TRUE);
-    if (stream) {
-        // Limit to READ_BUF_SIZE bytes to prevent overflows in the case of a poorly chosen command
-        account->password = g_malloc(READ_BUF_SIZE);
-        if (!account->password) {
-            log_error("Failed to allocate enough memory to read eval_password output");
-            return FALSE;
-        }
-        account->password = fgets(account->password, READ_BUF_SIZE, stream);
-        pclose(stream);
-        if (!account->password) {
-            log_error("No result from eval_password.");
-            return FALSE;
-        }
-
-        // strip trailing newline
-        if (g_str_has_suffix(account->password, "\n")) {
-            account->password[strlen(account->password)-1] = '\0';
-        }
-    } else {
-        log_error("popen failed when running eval_password.");
+    gchar **output = NULL;
+    gchar **argv = g_strsplit(account->eval_password, " ", 0);
+
+    if (!call_external(argv, &output, NULL)) {
+        g_strfreev(argv);
+        argv = NULL;
+        return FALSE;
+    }
+
+    g_strfreev(argv);
+
+    if (!output || !output[0]) {
+        log_error("Failed to read eval_password output");
+        g_strfreev(output);
+        output = NULL;
+        return FALSE;
+    }
+
+    account->password = strdup(output[0]);
+    g_strfreev(output);
+    output = NULL;
+
+    if (!account->password) {
+        log_error("Failed to allocate enough memory to read eval_password output");
         return FALSE;
     }
 
diff --git a/src/xmpp/avatar.c b/src/xmpp/avatar.c
index 701d6cb7..c5b44411 100644
--- a/src/xmpp/avatar.c
+++ b/src/xmpp/avatar.c
@@ -266,7 +266,10 @@ _avatar_request_item_result_handler(xmpp_stanza_t *const stanza, void *const use
 
     // if we shall open it
     if (g_hash_table_contains(shall_open, from_attr)) {
-        call_external(prefs_get_string(PREF_AVATAR_CMD), filename->str);
+        gchar *argv[] = {prefs_get_string(PREF_AVATAR_CMD), filename->str, NULL};
+        if (!call_external(argv, NULL, NULL)) {
+          cons_show_error("Unable to display avatar: check the logs for more information.");
+        }
         g_hash_table_remove(shall_open, from_attr);
     }